From dd13a0468a851b25cca9e578ffb0b4535e284f9f Mon Sep 17 00:00:00 2001 From: wlsh44 Date: Fri, 28 Jun 2024 16:48:27 +0900 Subject: [PATCH 01/14] =?UTF-8?q?[SCRUM-120]=20refactor:=20=EC=95=84?= =?UTF-8?q?=ED=8B=B0=EC=8A=A4=ED=8A=B8=20=ED=95=84=EC=9A=94=EC=97=86?= =?UTF-8?q?=EB=8A=94=20=ED=95=84=EB=93=9C=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../artist/dto/request/ArtistAddRequest.java | 13 +++---- .../dto/request/ArtistUpdateRequest.java | 4 +- .../dto/response/ArtistGetResponse.java | 4 +- .../dto/response/ArtistUpdateResponse.java | 4 +- .../server/domain/artist/entity/Artist.java | 37 ++++++++----------- .../impl/QueryDslArtistQueryRepository.java | 4 +- .../artist/service/ArtistCommandService.java | 3 +- .../record/dto/request/RecordGetResponse.java | 4 +- .../server/common/fixture/ArtistFixture.java | 35 +++++++++++++----- .../artist/api/ArtistControllerTest.java | 4 +- .../dto/request/ArtistAddRequestTest.java | 23 ++---------- .../ArtistCommandServiceIntegrationTest.java | 22 ++++------- .../service/ArtistCommandServiceTest.java | 8 ++-- .../service/ArtistQueryServiceTest.java | 3 +- 14 files changed, 70 insertions(+), 98 deletions(-) diff --git a/src/main/java/com/projectlyrics/server/domain/artist/dto/request/ArtistAddRequest.java b/src/main/java/com/projectlyrics/server/domain/artist/dto/request/ArtistAddRequest.java index 023685a7..8a43216f 100644 --- a/src/main/java/com/projectlyrics/server/domain/artist/dto/request/ArtistAddRequest.java +++ b/src/main/java/com/projectlyrics/server/domain/artist/dto/request/ArtistAddRequest.java @@ -1,17 +1,14 @@ package com.projectlyrics.server.domain.artist.dto.request; import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.Pattern; +import jakarta.validation.constraints.NotBlank; public record ArtistAddRequest( - @Schema(name = "아티스트의 한글 이름") - @Pattern(regexp = "^(?!\\s*$).+", message = "빈 문자열 또는 공백 문자열은 허용하지 않습니다.") + @NotBlank + @Schema(name = "아티스트의 이름") String name, - @Schema(name = "아티스트의 영어 이름") - @Pattern(regexp = "^(?!\\s*$).+", message = "빈 문자열 또는 공백 문자열은 허용하지 않습니다.") - String englishName, + @Schema(name = "아티스트의 cdn 이미지 경로") - @Pattern(regexp = "^https://.*", message = "이미지 경로는 https://로 시작해야 합니다.") - String profileImageCdnLink + String imageUrl ) { } diff --git a/src/main/java/com/projectlyrics/server/domain/artist/dto/request/ArtistUpdateRequest.java b/src/main/java/com/projectlyrics/server/domain/artist/dto/request/ArtistUpdateRequest.java index fa3ec116..68013577 100644 --- a/src/main/java/com/projectlyrics/server/domain/artist/dto/request/ArtistUpdateRequest.java +++ b/src/main/java/com/projectlyrics/server/domain/artist/dto/request/ArtistUpdateRequest.java @@ -5,9 +5,7 @@ public record ArtistUpdateRequest( @Schema(name = "아티스트의 한글 이름") String name, - @Schema(name = "아티스트의 영어 이름") - String englishName, - @Schema(name = "아티스트의 cdn 이미지 경로") + String profileImageCdnLink ) { } diff --git a/src/main/java/com/projectlyrics/server/domain/artist/dto/response/ArtistGetResponse.java b/src/main/java/com/projectlyrics/server/domain/artist/dto/response/ArtistGetResponse.java index fb7b7364..290810b0 100644 --- a/src/main/java/com/projectlyrics/server/domain/artist/dto/response/ArtistGetResponse.java +++ b/src/main/java/com/projectlyrics/server/domain/artist/dto/response/ArtistGetResponse.java @@ -6,7 +6,6 @@ public record ArtistGetResponse( Long id, String name, - String englishName, String profileImageCdnLink ) implements CursorResponse { @@ -14,8 +13,7 @@ public static ArtistGetResponse of(Artist artist) { return new ArtistGetResponse( artist.getId(), artist.getName(), - artist.getEnglishName(), - artist.getProfileImageCdnLink() + artist.getImageUrl() ); } diff --git a/src/main/java/com/projectlyrics/server/domain/artist/dto/response/ArtistUpdateResponse.java b/src/main/java/com/projectlyrics/server/domain/artist/dto/response/ArtistUpdateResponse.java index 51279b68..53bfa0ec 100644 --- a/src/main/java/com/projectlyrics/server/domain/artist/dto/response/ArtistUpdateResponse.java +++ b/src/main/java/com/projectlyrics/server/domain/artist/dto/response/ArtistUpdateResponse.java @@ -5,7 +5,6 @@ public record ArtistUpdateResponse( Long id, String name, - String englishName, String profileImageCdnLink ) { @@ -13,8 +12,7 @@ public static ArtistUpdateResponse from(Artist artist) { return new ArtistUpdateResponse( artist.getId(), artist.getName(), - artist.getEnglishName(), - artist.getProfileImageCdnLink() + artist.getImageUrl() ); } } diff --git a/src/main/java/com/projectlyrics/server/domain/artist/entity/Artist.java b/src/main/java/com/projectlyrics/server/domain/artist/entity/Artist.java index 5ac31873..1d7f1ce5 100644 --- a/src/main/java/com/projectlyrics/server/domain/artist/entity/Artist.java +++ b/src/main/java/com/projectlyrics/server/domain/artist/entity/Artist.java @@ -10,6 +10,7 @@ import jakarta.persistence.Id; import jakarta.persistence.Table; +import java.util.Optional; import java.util.function.Consumer; import lombok.*; @@ -29,45 +30,39 @@ public class Artist extends BaseEntity { @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; - @Column(nullable = true) + @Column(nullable = false) private String name; - @Column(nullable = true) - private String englishName; + @Column + private String imageUrl; - @Column(nullable = true) - private String profileImageCdnLink; + public static Artist of(String name, String imageUrl) { + return new Artist(name, imageUrl); + } public static Artist from(ArtistAddRequest dto) { - checkString(dto.name()); - checkString(dto.englishName()); - checkUrl(dto.profileImageCdnLink()); - return new Artist( dto.name(), - dto.englishName(), - dto.profileImageCdnLink() + dto.imageUrl() ); } - private Artist(String name, String englishName, String profileImageCdnLink) { + private Artist(String name, String imageUrl) { + checkString(name); + Optional.ofNullable(imageUrl) + .ifPresent(DomainUtils::checkUrl); this.name = name; - this.englishName = englishName; - this.profileImageCdnLink = profileImageCdnLink; + this.imageUrl = imageUrl; } public void updateName(String name) { this.name = name; } - public void updateEnglishName(String englishName) { - this.englishName = englishName; - } - - public void updateProfileImageCdnLink(String profileImageCdnLink) { - DomainUtils.checkUrl(profileImageCdnLink); + public void updateImageUrl(String imageUrl) { + DomainUtils.checkUrl(imageUrl); - this.profileImageCdnLink = profileImageCdnLink; + this.imageUrl = imageUrl; } public void updateIfNotBlank(String value, Consumer updater) { diff --git a/src/main/java/com/projectlyrics/server/domain/artist/repository/impl/QueryDslArtistQueryRepository.java b/src/main/java/com/projectlyrics/server/domain/artist/repository/impl/QueryDslArtistQueryRepository.java index 876a8903..5d0aed91 100644 --- a/src/main/java/com/projectlyrics/server/domain/artist/repository/impl/QueryDslArtistQueryRepository.java +++ b/src/main/java/com/projectlyrics/server/domain/artist/repository/impl/QueryDslArtistQueryRepository.java @@ -58,8 +58,8 @@ public Slice findAllByQueryAndNotDeleted(String query, Long cursor, Page goeCursorId(cursor), QArtist.artist.deletedAt.isNull(), anyOf( - QArtist.artist.name.contains(query), - QArtist.artist.englishName.contains(query)) + QArtist.artist.name.contains(query) + ) ) .limit(pageable.getPageSize() + 1) .fetch(); diff --git a/src/main/java/com/projectlyrics/server/domain/artist/service/ArtistCommandService.java b/src/main/java/com/projectlyrics/server/domain/artist/service/ArtistCommandService.java index 82f0aabf..3876187c 100644 --- a/src/main/java/com/projectlyrics/server/domain/artist/service/ArtistCommandService.java +++ b/src/main/java/com/projectlyrics/server/domain/artist/service/ArtistCommandService.java @@ -33,8 +33,7 @@ public ArtistUpdateResponse updateArtist(Long artistId, ArtistUpdateRequest requ .orElseThrow(ArtistNotFoundException::new); artist.updateIfNotBlank(request.name(), artist::updateName); - artist.updateIfNotBlank(request.englishName(), artist::updateEnglishName); - artist.updateIfNotBlank(request.profileImageCdnLink(), artist::updateProfileImageCdnLink); + artist.updateIfNotBlank(request.profileImageCdnLink(), artist::updateImageUrl); return ArtistUpdateResponse.from(artist); } diff --git a/src/main/java/com/projectlyrics/server/domain/record/dto/request/RecordGetResponse.java b/src/main/java/com/projectlyrics/server/domain/record/dto/request/RecordGetResponse.java index 7a0b7f48..f92a3b79 100644 --- a/src/main/java/com/projectlyrics/server/domain/record/dto/request/RecordGetResponse.java +++ b/src/main/java/com/projectlyrics/server/domain/record/dto/request/RecordGetResponse.java @@ -6,7 +6,6 @@ public record RecordGetResponse( Long id, String artistName, - String artistEnglishName, String artistProfileImageCdnLink ) implements CursorResponse { @@ -14,8 +13,7 @@ public static RecordGetResponse of(Record record) { return new RecordGetResponse( record.getId(), record.getArtist().getName(), - record.getArtist().getEnglishName(), - record.getArtist().getProfileImageCdnLink() + record.getArtist().getImageUrl() ); } diff --git a/src/test/java/com/projectlyrics/server/common/fixture/ArtistFixture.java b/src/test/java/com/projectlyrics/server/common/fixture/ArtistFixture.java index 39440239..511234ea 100644 --- a/src/test/java/com/projectlyrics/server/common/fixture/ArtistFixture.java +++ b/src/test/java/com/projectlyrics/server/common/fixture/ArtistFixture.java @@ -5,19 +5,34 @@ public class ArtistFixture { + private String name = "아티스트 이름"; + private String imageUrl = "https://asdf.com"; + public static Artist create() { - return Artist.from(new ArtistAddRequest( - "넬", - "NELL", - "https://~" - )); + return Artist.of("아티스트 이름", "https://asdf.com"); } public static Artist createWithName(String name) { - return Artist.from(new ArtistAddRequest( - name, - "NELL", - "https://~" - )); + return Artist.of(name, "https://asdf.com"); + } + + private ArtistFixture() {} + + public static ArtistFixture builder() { + return new ArtistFixture(); + } + + public ArtistFixture name(String name) { + this.name = name; + return this; + } + + public ArtistFixture imageUrl(String imageUrl) { + this.imageUrl = imageUrl; + return this; + } + + public Artist build() { + return Artist.of(name, imageUrl); } } diff --git a/src/test/java/com/projectlyrics/server/domain/artist/api/ArtistControllerTest.java b/src/test/java/com/projectlyrics/server/domain/artist/api/ArtistControllerTest.java index db6b820a..1f25a7da 100644 --- a/src/test/java/com/projectlyrics/server/domain/artist/api/ArtistControllerTest.java +++ b/src/test/java/com/projectlyrics/server/domain/artist/api/ArtistControllerTest.java @@ -20,7 +20,7 @@ class ArtistControllerTest extends ControllerTest { @WithMockUser void 아티스트를_추가해야_한다() throws Exception { //given - ArtistAddRequest request = new ArtistAddRequest("라디오헤드", "radiohead", "https://lll.kk"); + ArtistAddRequest request = new ArtistAddRequest("라디오헤드", "https://lll.kk"); //when then mockMvc.perform(post("/api/v1/artists") @@ -36,7 +36,7 @@ class ArtistControllerTest extends ControllerTest { @WithMockUser void 아티스트를_수정해야_한다() throws Exception { //given - ArtistUpdateRequest request = new ArtistUpdateRequest("라디오헤드", "radiohead", "https://kkk.ll"); + ArtistUpdateRequest request = new ArtistUpdateRequest("라디오헤드", "https://kkk.ll"); //when then mockMvc.perform(patch("/api/v1/artists/{artistId}", 1) diff --git a/src/test/java/com/projectlyrics/server/domain/artist/dto/request/ArtistAddRequestTest.java b/src/test/java/com/projectlyrics/server/domain/artist/dto/request/ArtistAddRequestTest.java index b12a7172..8d851a89 100644 --- a/src/test/java/com/projectlyrics/server/domain/artist/dto/request/ArtistAddRequestTest.java +++ b/src/test/java/com/projectlyrics/server/domain/artist/dto/request/ArtistAddRequestTest.java @@ -19,22 +19,7 @@ class ArtistAddRequestTest { @ValueSource(strings = {"", " "}) void 아티스트의_이름이_빈_문자열이나_공백_문자열이라면_validation_에러가_발생한다(String name) { // given - ArtistAddRequest addArtistRequest = createAddArtistRequest(name, "NELL", "https://~"); - - // when - Set> violation = validator.validate(addArtistRequest); - - // then - violation.forEach(error -> { - assertThat(error.getMessage()).isEqualTo("빈 문자열 또는 공백 문자열은 허용하지 않습니다."); - }); - } - - @ParameterizedTest - @ValueSource(strings = {"", " "}) - void 아티스트의_영어_이름이_문자열이나_빈_문자열_또는_공백_문자열이라면_validation_에러가_발생한다(String englishName) { - // given - ArtistAddRequest addArtistRequest = createAddArtistRequest("넬", englishName, "https://~"); + ArtistAddRequest addArtistRequest = createAddArtistRequest(name, "https://~"); // when Set> violation = validator.validate(addArtistRequest); @@ -48,7 +33,7 @@ class ArtistAddRequestTest { @Test void 아티스트의_이미지_경로가_https로_시작하지_않는_문자열이라면_validation_에러가_발생한다() { // given - ArtistAddRequest addArtistRequest = createAddArtistRequest("넬", "NELL", "imageUrl"); + ArtistAddRequest addArtistRequest = createAddArtistRequest("넬", "imageUrl"); // when Set> violation = validator.validate(addArtistRequest); @@ -59,7 +44,7 @@ class ArtistAddRequestTest { }); } - private ArtistAddRequest createAddArtistRequest(String name, String englishName, String profileImageCdnLink) { - return new ArtistAddRequest(name, englishName, profileImageCdnLink); + private ArtistAddRequest createAddArtistRequest(String name, String profileImageCdnLink) { + return new ArtistAddRequest(name, profileImageCdnLink); } } \ No newline at end of file diff --git a/src/test/java/com/projectlyrics/server/domain/artist/service/ArtistCommandServiceIntegrationTest.java b/src/test/java/com/projectlyrics/server/domain/artist/service/ArtistCommandServiceIntegrationTest.java index 8b6b2198..4e4926bf 100644 --- a/src/test/java/com/projectlyrics/server/domain/artist/service/ArtistCommandServiceIntegrationTest.java +++ b/src/test/java/com/projectlyrics/server/domain/artist/service/ArtistCommandServiceIntegrationTest.java @@ -10,14 +10,8 @@ import com.projectlyrics.server.domain.artist.exception.ArtistNotFoundException; import com.projectlyrics.server.domain.artist.repository.ArtistCommandRepository; import com.projectlyrics.server.domain.artist.repository.ArtistQueryRepository; -import com.projectlyrics.server.domain.artist.repository.impl.JpaArtistCommandRepository; import com.projectlyrics.server.domain.common.message.ErrorCode; -import com.projectlyrics.server.global.exception.FeelinException; -import org.assertj.core.api.Assertions; -import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import org.mockito.InjectMocks; -import org.mockito.Mock; import org.springframework.beans.factory.annotation.Autowired; import static org.assertj.core.api.Assertions.*; @@ -46,8 +40,7 @@ class ArtistCommandServiceIntegrationTest extends IntegrationTest { Artist artist = artistQueryRepository.findByIdAndNotDeleted(response.id()).get(); assertAll( () -> assertThat(artist.getName()).isEqualTo(request.name()), - () -> assertThat(artist.getEnglishName()).isEqualTo(request.englishName()), - () -> assertThat(artist.getProfileImageCdnLink()).isEqualTo(request.profileImageCdnLink()), + () -> assertThat(artist.getImageUrl()).isEqualTo(request.imageUrl()), () -> assertThat(artist.isInUse()).isTrue() ); } @@ -56,7 +49,7 @@ class ArtistCommandServiceIntegrationTest extends IntegrationTest { void 아티스트를_수정한다() throws Exception { //given Artist savedArtist = artistCommandRepository.save(ArtistFixture.create()); - ArtistUpdateRequest request = createUpdateArtistRequest("너드커넥션", "Nerd Connection", "https://~2"); + ArtistUpdateRequest request = createUpdateArtistRequest("너드커넥션", "https://~2"); //when ArtistUpdateResponse response = sut.updateArtist(savedArtist.getId(), request); @@ -65,15 +58,14 @@ class ArtistCommandServiceIntegrationTest extends IntegrationTest { Artist artist = artistQueryRepository.findByIdAndNotDeleted(response.id()).get(); assertAll( () -> assertThat(artist.getName()).isEqualTo(request.name()), - () -> assertThat(artist.getEnglishName()).isEqualTo(request.englishName()), - () -> assertThat(artist.getProfileImageCdnLink()).isEqualTo(request.profileImageCdnLink()) + () -> assertThat(artist.getImageUrl()).isEqualTo(request.profileImageCdnLink()) ); } @Test void 없는_아티스트를_수정할_경우_예외가_발생해야_한다() throws Exception { //given - ArtistUpdateRequest request = createUpdateArtistRequest("너드커넥션", "Nerd Connection", "https://~2"); + ArtistUpdateRequest request = createUpdateArtistRequest("너드커넥션", "https://~2"); //when Throwable throwable = catchThrowable(() -> sut.updateArtist(1L, request)); @@ -84,10 +76,10 @@ class ArtistCommandServiceIntegrationTest extends IntegrationTest { } private ArtistAddRequest createAddArtistRequest() { - return new ArtistAddRequest("넬", "NELL", "https://~"); + return new ArtistAddRequest("넬", "https://~"); } - private ArtistUpdateRequest createUpdateArtistRequest(String name, String englishName, String profileImageCdnLink) { - return new ArtistUpdateRequest(name, englishName, profileImageCdnLink); + private ArtistUpdateRequest createUpdateArtistRequest(String name, String profileImageCdnLink) { + return new ArtistUpdateRequest(name, profileImageCdnLink); } } diff --git a/src/test/java/com/projectlyrics/server/domain/artist/service/ArtistCommandServiceTest.java b/src/test/java/com/projectlyrics/server/domain/artist/service/ArtistCommandServiceTest.java index 7e0b2a57..9d7665b6 100644 --- a/src/test/java/com/projectlyrics/server/domain/artist/service/ArtistCommandServiceTest.java +++ b/src/test/java/com/projectlyrics/server/domain/artist/service/ArtistCommandServiceTest.java @@ -56,8 +56,7 @@ class ArtistCommandServiceTest { then(artistCommandRepository).should().save(addArtistArgumentCaptor.capture()); Artist captorValue = addArtistArgumentCaptor.getValue(); assertThat(addArtistRequest.name()).isEqualTo(captorValue.getName()); - assertThat(addArtistRequest.englishName()).isEqualTo(captorValue.getEnglishName()); - assertThat(addArtistRequest.profileImageCdnLink()).isEqualTo(captorValue.getProfileImageCdnLink()); + assertThat(addArtistRequest.imageUrl()).isEqualTo(captorValue.getImageUrl()); } @Test @@ -74,7 +73,6 @@ class ArtistCommandServiceTest { // then then(artistQueryRepository).should().findByIdAndNotDeleted(anyLong()); assertThat(updateArtistResponse.name()).isEqualTo(artist.getName()); - assertThat(updateArtistResponse.englishName()).isEqualTo(artist.getEnglishName()); assertThat(updateArtistResponse.profileImageCdnLink()).isEqualTo(updateArtistRequest.profileImageCdnLink()); } @@ -112,10 +110,10 @@ class ArtistCommandServiceTest { } private ArtistAddRequest createAddArtistRequest() { - return new ArtistAddRequest("넬", "NELL", "https://~"); + return new ArtistAddRequest("넬", "https://~"); } private ArtistUpdateRequest createUpdateArtistRequest(String name, String englishName, String profileImageCdnLink) { - return new ArtistUpdateRequest(name, englishName, profileImageCdnLink); + return new ArtistUpdateRequest(name, profileImageCdnLink); } } \ No newline at end of file diff --git a/src/test/java/com/projectlyrics/server/domain/artist/service/ArtistQueryServiceTest.java b/src/test/java/com/projectlyrics/server/domain/artist/service/ArtistQueryServiceTest.java index 6e540430..a641852b 100644 --- a/src/test/java/com/projectlyrics/server/domain/artist/service/ArtistQueryServiceTest.java +++ b/src/test/java/com/projectlyrics/server/domain/artist/service/ArtistQueryServiceTest.java @@ -50,8 +50,7 @@ class ArtistQueryServiceTest { then(artistQueryRepository).should().findByIdAndNotDeleted(anyLong()); assertThat(getArtistResponse.id()).isEqualTo(artistId); assertThat(getArtistResponse.name()).isEqualTo(artist.getName()); - assertThat(getArtistResponse.englishName()).isEqualTo(artist.getEnglishName()); - assertThat(getArtistResponse.profileImageCdnLink()).isEqualTo(artist.getProfileImageCdnLink()); + assertThat(getArtistResponse.profileImageCdnLink()).isEqualTo(artist.getImageUrl()); } @Test From 3d88589039bcfd9e4380bc7ce4e6a87c4e3ca5aa Mon Sep 17 00:00:00 2001 From: wlsh44 Date: Fri, 28 Jun 2024 16:49:48 +0900 Subject: [PATCH 02/14] =?UTF-8?q?[SCRUM-120]=20refactor:=20=EC=95=BD?= =?UTF-8?q?=EA=B4=80=20API=20=EB=A6=AC=ED=8C=A9=ED=84=B0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/dto/request/AuthSignUpRequest.java | 10 +++++-- .../domain/user/entity/TermsAgreements.java | 28 +++++++++++++++++-- .../server/domain/user/entity/User.java | 18 ++++++++---- .../server/common/fixture/UserFixture.java | 10 ++++--- .../domain/auth/api/AuthControllerTest.java | 7 +++-- .../AuthCommandServiceIntegrationTest.java | 7 +++-- .../server/domain/user/entity/UserTest.java | 9 +++--- 7 files changed, 64 insertions(+), 25 deletions(-) diff --git a/src/main/java/com/projectlyrics/server/domain/auth/dto/request/AuthSignUpRequest.java b/src/main/java/com/projectlyrics/server/domain/auth/dto/request/AuthSignUpRequest.java index 7e6c46fe..d7886aec 100644 --- a/src/main/java/com/projectlyrics/server/domain/auth/dto/request/AuthSignUpRequest.java +++ b/src/main/java/com/projectlyrics/server/domain/auth/dto/request/AuthSignUpRequest.java @@ -10,6 +10,7 @@ import org.springframework.format.annotation.DateTimeFormat; import java.time.Year; +import java.util.List; public record AuthSignUpRequest( @NotBlank @@ -33,7 +34,7 @@ public record AuthSignUpRequest( Year birthYear, @Valid - TermsInput terms + List terms ) { public record TermsInput( @@ -42,8 +43,11 @@ public record TermsInput( boolean agree, @NotNull - @Schema(description = "약관 내용") + @Schema(description = "약관 제목") + String title, - String agreement) { + @Schema(description = "약관 내용 링크") + String agreement + ) { } } diff --git a/src/main/java/com/projectlyrics/server/domain/user/entity/TermsAgreements.java b/src/main/java/com/projectlyrics/server/domain/user/entity/TermsAgreements.java index 70bb3eb9..d4b0e07c 100644 --- a/src/main/java/com/projectlyrics/server/domain/user/entity/TermsAgreements.java +++ b/src/main/java/com/projectlyrics/server/domain/user/entity/TermsAgreements.java @@ -3,28 +3,50 @@ import com.projectlyrics.server.domain.auth.exception.NotAgreeToTermsException; import jakarta.persistence.Column; import jakarta.persistence.Embeddable; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.ManyToOne; import lombok.AccessLevel; import lombok.NoArgsConstructor; +import static com.projectlyrics.server.domain.common.util.DomainUtils.checkNull; import static com.projectlyrics.server.domain.common.util.DomainUtils.checkString; -@Embeddable +@Entity @NoArgsConstructor(access = AccessLevel.PROTECTED) public class TermsAgreements { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + @Column(nullable = false) private boolean agree; + @Column(nullable = false) + private String title; + @Column(nullable = false) private String agreement; + @ManyToOne(fetch = FetchType.LAZY) + private User user; - public TermsAgreements(boolean agree, String agreement) { + public TermsAgreements(boolean agree, String title, String agreement) { if (!agree) { throw new NotAgreeToTermsException(); } - checkString(agreement); + checkString(title); this.agree = agree; + this.title = title; this.agreement = agreement; } + + void setUser(User user) { + checkNull(user); + this.user = user; + } } diff --git a/src/main/java/com/projectlyrics/server/domain/user/entity/User.java b/src/main/java/com/projectlyrics/server/domain/user/entity/User.java index fd4e0f14..bc183299 100644 --- a/src/main/java/com/projectlyrics/server/domain/user/entity/User.java +++ b/src/main/java/com/projectlyrics/server/domain/user/entity/User.java @@ -16,6 +16,7 @@ import jakarta.persistence.GenerationType; import jakarta.persistence.Id; import jakarta.persistence.JoinColumn; +import jakarta.persistence.OneToMany; import jakarta.persistence.OneToOne; import jakarta.persistence.Table; import lombok.AccessLevel; @@ -23,6 +24,8 @@ import lombok.Getter; import lombok.NoArgsConstructor; +import java.util.List; + import static com.projectlyrics.server.domain.common.util.DomainUtils.checkNull; import static com.projectlyrics.server.domain.common.util.DomainUtils.checkString; @@ -50,31 +53,36 @@ public class User extends BaseEntity { @Embedded private UserMetaInfo info; - @Embedded - private TermsAgreements termsAgreements; + @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true) + private List termsAgreements; - private User(String email, Auth auth, String username, Gender gender, int birthYear, TermsAgreements termsAgreements) { + private User(String email, Auth auth, String username, Gender gender, int birthYear, List termsAgreements) { checkString(email); checkNull(auth); + checkNull(termsAgreements); this.email = email; this.auth = auth; this.username = new Username(username); this.info = new UserMetaInfo(gender, birthYear); this.termsAgreements = termsAgreements; + termsAgreements.forEach(terms -> terms.setUser(this)); } public static User createUser(AuthSocialInfo socialInfo, AuthSignUpRequest request) { + List termsList = request.terms().stream() + .map(termsInput -> new TermsAgreements(termsInput.agree(), termsInput.title(), termsInput.agreement())) + .toList(); return new User( socialInfo.email(), Auth.of(socialInfo.authProvider(), Role.USER, socialInfo.socialId()), request.username(), request.gender(), request.birthYear().getValue(), - new TermsAgreements(request.terms().agree(), request.terms().agreement()) + termsList ); } - public static User of(String email, Auth auth, String username, Gender gender, int birthYear, TermsAgreements termsAgreements) { + public static User of(String email, Auth auth, String username, Gender gender, int birthYear, List termsAgreements) { return new User(email, auth, username, gender, birthYear, termsAgreements); } } diff --git a/src/test/java/com/projectlyrics/server/common/fixture/UserFixture.java b/src/test/java/com/projectlyrics/server/common/fixture/UserFixture.java index 90e6e925..7a61a7b8 100644 --- a/src/test/java/com/projectlyrics/server/common/fixture/UserFixture.java +++ b/src/test/java/com/projectlyrics/server/common/fixture/UserFixture.java @@ -8,6 +8,8 @@ import com.projectlyrics.server.domain.user.entity.User; import com.projectlyrics.server.domain.user.entity.Username; +import java.util.List; + public class UserFixture { private String email = "test@test.com"; @@ -15,7 +17,7 @@ public class UserFixture { private String username = "username"; private Gender gender = Gender.MALE; private int birthYear = 1999; - private TermsAgreements termsAgreements = new TermsAgreements(true, "agreement"); + private List termsAgreements = List.of(new TermsAgreements(true, "title", "agreement")); private UserFixture() {} @@ -26,8 +28,8 @@ public static User create() { "username", Gender.MALE, 1999, - new TermsAgreements(true, "agreement") - ); + List.of(new TermsAgreements(true, "title", "agreement") + )); } public static UserFixture builder() { @@ -74,7 +76,7 @@ public UserFixture birth(int birthYear) { } public UserFixture terms(TermsAgreements termsAgreements) { - this.termsAgreements = termsAgreements; + this.termsAgreements.add(termsAgreements); return this; } } diff --git a/src/test/java/com/projectlyrics/server/domain/auth/api/AuthControllerTest.java b/src/test/java/com/projectlyrics/server/domain/auth/api/AuthControllerTest.java index 7fd3286a..a1030dde 100644 --- a/src/test/java/com/projectlyrics/server/domain/auth/api/AuthControllerTest.java +++ b/src/test/java/com/projectlyrics/server/domain/auth/api/AuthControllerTest.java @@ -15,6 +15,7 @@ import org.springframework.http.MediaType; import java.time.Year; +import java.util.List; import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; @@ -86,7 +87,7 @@ class AuthControllerTest extends ControllerTest { "username", Gender.MALE, Year.of(1999), - new AuthSignUpRequest.TermsInput(true, "agreement") + List.of(new AuthSignUpRequest.TermsInput(true, "title", "agreement")) ); AuthTokenResponse response = new AuthTokenResponse("access token", "refresh token"); given(authCommandService.signUp(any())) @@ -110,7 +111,7 @@ class AuthControllerTest extends ControllerTest { "username", Gender.MALE, Year.of(1999), - new AuthSignUpRequest.TermsInput(false, "agreement") + List.of(new AuthSignUpRequest.TermsInput(false, "title", "agreement")) ); NotAgreeToTermsException e = new NotAgreeToTermsException(); ErrorResponse response = ErrorResponse.of(e.getErrorCode()); @@ -135,7 +136,7 @@ class AuthControllerTest extends ControllerTest { "username", Gender.MALE, Year.of(1999), - new AuthSignUpRequest.TermsInput(false, "agreement") + List.of(new AuthSignUpRequest.TermsInput(true, "title", "agreement")) ); ErrorResponse response = ErrorResponse.of(ErrorCode.INVALID_TOKEN); given(authCommandService.signUp(any())) diff --git a/src/test/java/com/projectlyrics/server/domain/auth/service/AuthCommandServiceIntegrationTest.java b/src/test/java/com/projectlyrics/server/domain/auth/service/AuthCommandServiceIntegrationTest.java index 7c4ee6e5..c6b6d9a2 100644 --- a/src/test/java/com/projectlyrics/server/domain/auth/service/AuthCommandServiceIntegrationTest.java +++ b/src/test/java/com/projectlyrics/server/domain/auth/service/AuthCommandServiceIntegrationTest.java @@ -35,6 +35,7 @@ import org.springframework.boot.test.mock.mockito.SpyBean; import java.time.Year; +import java.util.List; import java.util.Optional; public class AuthCommandServiceIntegrationTest extends IntegrationTest { @@ -124,7 +125,7 @@ public class AuthCommandServiceIntegrationTest extends IntegrationTest { "username", Gender.MALE, Year.of(1999), - new AuthSignUpRequest.TermsInput(true, "agreement") + List.of(new AuthSignUpRequest.TermsInput(true, "title", "agreement")) ); doReturn(new KakaoUserInfoResponse(user.getAuth().getSocialId(), new KakaoAccount(user.getEmail()))) .when(kakaoSocialDataApiClient).getUserInfo(any()); @@ -148,7 +149,7 @@ public class AuthCommandServiceIntegrationTest extends IntegrationTest { "username", Gender.MALE, Year.of(1999), - new AuthSignUpRequest.TermsInput(false, "agreement") + List.of(new AuthSignUpRequest.TermsInput(false, "title", "agreement")) ); doReturn(new KakaoUserInfoResponse(user.getAuth().getSocialId(), new KakaoAccount(user.getEmail()))) .when(kakaoSocialDataApiClient).getUserInfo(any()); @@ -167,7 +168,7 @@ public class AuthCommandServiceIntegrationTest extends IntegrationTest { "username", Gender.MALE, Year.of(1999), - new AuthSignUpRequest.TermsInput(true, "agreement") + List.of(new AuthSignUpRequest.TermsInput(true, "title", "agreement")) ); //when then diff --git a/src/test/java/com/projectlyrics/server/domain/user/entity/UserTest.java b/src/test/java/com/projectlyrics/server/domain/user/entity/UserTest.java index 5ebeb910..c60616e2 100644 --- a/src/test/java/com/projectlyrics/server/domain/user/entity/UserTest.java +++ b/src/test/java/com/projectlyrics/server/domain/user/entity/UserTest.java @@ -11,6 +11,7 @@ import org.junit.jupiter.api.Test; import java.time.Year; +import java.util.List; import static org.assertj.core.api.Assertions.*; import static org.junit.jupiter.api.Assertions.*; @@ -29,7 +30,7 @@ class UserTest { "username", Gender.MALE, Year.of(1999), - new AuthSignUpRequest.TermsInput(true, "agreement") + List.of(new AuthSignUpRequest.TermsInput(true, "title", "agreement")) ))); } @@ -46,7 +47,7 @@ class UserTest { tooLongUsername, Gender.MALE, Year.of(1999), - new AuthSignUpRequest.TermsInput(true, "agreement") + List.of(new AuthSignUpRequest.TermsInput(true, "title", "agreement")) ))) .isInstanceOf(InvalidUsernameException.class); } @@ -54,7 +55,7 @@ class UserTest { @Test void 유저를_생성할_때_약관동의하지_않으면_예외가_발생해야_한다() throws Exception { //given - AuthSignUpRequest.TermsInput agreement = new AuthSignUpRequest.TermsInput(false, "agreement"); + List agreement = List.of(new AuthSignUpRequest.TermsInput(false, "title", "agreement")); //when then assertThatThrownBy(() -> User.createUser(new AuthSocialInfo(AuthProvider.KAKAO, "socialId", "email"), @@ -82,7 +83,7 @@ class UserTest { "username", Gender.MALE, year, - new AuthSignUpRequest.TermsInput(true, "agreement") + List.of(new AuthSignUpRequest.TermsInput(true, "title", "agreement")) ))) .isInstanceOf(InvalidAgeException.class); } From 969bf72fc3e93b26e56a36c4b2bf147299ef1607 Mon Sep 17 00:00:00 2001 From: wlsh44 Date: Fri, 28 Jun 2024 16:50:38 +0900 Subject: [PATCH 03/14] =?UTF-8?q?[SCRUM-120]=20refactor:=20=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=95=20=EA=B4=80=EB=A0=A8=20=EB=A6=AC=ED=8C=A9?= =?UTF-8?q?=ED=84=B0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../server/domain/artist/api/ArtistControllerSwagger.java | 2 +- .../domain/common/dto/util/CursorBasePaginatedResponse.java | 6 ++++-- .../server/domain/common/util/QueryDslUtils.java | 6 +++--- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/projectlyrics/server/domain/artist/api/ArtistControllerSwagger.java b/src/main/java/com/projectlyrics/server/domain/artist/api/ArtistControllerSwagger.java index 54bf2977..914ba52a 100644 --- a/src/main/java/com/projectlyrics/server/domain/artist/api/ArtistControllerSwagger.java +++ b/src/main/java/com/projectlyrics/server/domain/artist/api/ArtistControllerSwagger.java @@ -55,7 +55,7 @@ public ResponseEntity getArtist( description = "아티스트의 데이터의 목록을 조회합니다." ) public ResponseEntity> getArtistList( - @Parameter(description = "이전에 응답 받은 nextCursor 값. 응답 받은 값이 없다면 해당 값을 비워서 요청합니다.") + @Parameter(description = "이전에 응답 받은 cursor 값. 응답 받은 값이 없다면 해당 값을 비워서 요청합니다.") @RequestParam(required = false) Long cursor, @Parameter(description = "조회할 데이터의 최대 크기") @RequestParam(defaultValue = "10") int size diff --git a/src/main/java/com/projectlyrics/server/domain/common/dto/util/CursorBasePaginatedResponse.java b/src/main/java/com/projectlyrics/server/domain/common/dto/util/CursorBasePaginatedResponse.java index 2f076897..6938c537 100644 --- a/src/main/java/com/projectlyrics/server/domain/common/dto/util/CursorBasePaginatedResponse.java +++ b/src/main/java/com/projectlyrics/server/domain/common/dto/util/CursorBasePaginatedResponse.java @@ -7,12 +7,14 @@ import org.springframework.data.domain.Slice; public record CursorBasePaginatedResponse( - @Schema(description = "다음 페이지를 요청하기 위한 커서 값") boolean hasNext, + @Schema(description = "다음 페이지를 요청하기 위한 커서 값") Long cursor, + @Schema(description = "다음 페이지 존재 여부") boolean hasNext, @Schema(description = "요청한 데이터") List data ) { - public static CursorBasePaginatedResponse of(Slice slice) { + public static CursorBasePaginatedResponse of(Slice slice) { return new CursorBasePaginatedResponse<>( + slice.getContent().isEmpty() ? null : slice.getContent().getLast().getId(), slice.hasNext(), slice.getContent() ); diff --git a/src/main/java/com/projectlyrics/server/domain/common/util/QueryDslUtils.java b/src/main/java/com/projectlyrics/server/domain/common/util/QueryDslUtils.java index cea850fc..a7adc5ff 100644 --- a/src/main/java/com/projectlyrics/server/domain/common/util/QueryDslUtils.java +++ b/src/main/java/com/projectlyrics/server/domain/common/util/QueryDslUtils.java @@ -1,16 +1,16 @@ package com.projectlyrics.server.domain.common.util; -import com.projectlyrics.server.domain.artist.entity.QArtist; import com.querydsl.core.types.dsl.BooleanExpression; import java.util.List; +import com.querydsl.core.types.dsl.NumberPath; import org.springframework.data.domain.Pageable; public class QueryDslUtils { - public static BooleanExpression goeCursorId(Long cursor) { - return cursor == null ? null : QArtist.artist.id.goe(cursor); + public static BooleanExpression gtCursorId(Long cursor, NumberPath id) { + return cursor == null ? null : id.gt(cursor); } public static boolean checkIfHasNext(Pageable pageable, List content) { From 85308ba56aac8383552853a86b52fff7b1374c1b Mon Sep 17 00:00:00 2001 From: wlsh44 Date: Fri, 28 Jun 2024 16:53:48 +0900 Subject: [PATCH 04/14] =?UTF-8?q?[SCRUM-120]=20feat:=20=EA=B4=80=EC=8B=AC?= =?UTF-8?q?=20=EC=95=84=ED=8B=B0=EC=8A=A4=ED=8A=B8=20=EB=93=B1=EB=A1=9D=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/ArtistQueryRepository.java | 3 ++ .../impl/QueryDslArtistQueryRepository.java | 10 ++++ .../artist/service/ArtistQueryService.java | 6 +++ .../api/FavoriteArtistController.java | 31 ++++++++++++ .../CreateFavoriteArtistListRequest.java | 11 +++++ .../favoriteartist/entity/FavoriteArtist.java | 48 +++++++++++++++++++ .../FavoriteArtistCommandRepository.java | 9 ++++ .../FavoriteArtistQueryRepository.java | 9 ++++ ...QueryDslFavoriteArtistQueryRepository.java | 40 ++++++++++++++++ .../service/FavoriteArtistCommandService.java | 33 +++++++++++++ 10 files changed, 200 insertions(+) create mode 100644 src/main/java/com/projectlyrics/server/domain/favoriteartist/api/FavoriteArtistController.java create mode 100644 src/main/java/com/projectlyrics/server/domain/favoriteartist/dto/request/CreateFavoriteArtistListRequest.java create mode 100644 src/main/java/com/projectlyrics/server/domain/favoriteartist/entity/FavoriteArtist.java create mode 100644 src/main/java/com/projectlyrics/server/domain/favoriteartist/repository/FavoriteArtistCommandRepository.java create mode 100644 src/main/java/com/projectlyrics/server/domain/favoriteartist/repository/FavoriteArtistQueryRepository.java create mode 100644 src/main/java/com/projectlyrics/server/domain/favoriteartist/repository/impl/QueryDslFavoriteArtistQueryRepository.java create mode 100644 src/main/java/com/projectlyrics/server/domain/favoriteartist/service/FavoriteArtistCommandService.java diff --git a/src/main/java/com/projectlyrics/server/domain/artist/repository/ArtistQueryRepository.java b/src/main/java/com/projectlyrics/server/domain/artist/repository/ArtistQueryRepository.java index 5ec1e615..e3c8ebb5 100644 --- a/src/main/java/com/projectlyrics/server/domain/artist/repository/ArtistQueryRepository.java +++ b/src/main/java/com/projectlyrics/server/domain/artist/repository/ArtistQueryRepository.java @@ -2,6 +2,7 @@ import com.projectlyrics.server.domain.artist.entity.Artist; +import java.util.List; import java.util.Optional; import org.springframework.data.domain.Pageable; @@ -14,4 +15,6 @@ public interface ArtistQueryRepository { Slice findAllByQueryAndNotDeleted(String query, Long cursor, Pageable pageable); Slice findAllAndNotDeleted(Long cursor, Pageable pageable); + + List findAllByIds(List artistIds); } diff --git a/src/main/java/com/projectlyrics/server/domain/artist/repository/impl/QueryDslArtistQueryRepository.java b/src/main/java/com/projectlyrics/server/domain/artist/repository/impl/QueryDslArtistQueryRepository.java index 5d0aed91..c27d4907 100644 --- a/src/main/java/com/projectlyrics/server/domain/artist/repository/impl/QueryDslArtistQueryRepository.java +++ b/src/main/java/com/projectlyrics/server/domain/artist/repository/impl/QueryDslArtistQueryRepository.java @@ -50,6 +50,16 @@ public Slice findAllAndNotDeleted(Long cursor, Pageable pageable) { return new SliceImpl<>(content, pageable, QueryDslUtils.checkIfHasNext(pageable, content)); } + @Override + public List findAllByIds(List artistIds) { + return jpaQueryFactory.selectFrom(QArtist.artist) + .where( + QArtist.artist.id.in(artistIds), + QArtist.artist.deletedAt.isNull() + ) + .fetch(); + } + @Override public Slice findAllByQueryAndNotDeleted(String query, Long cursor, Pageable pageable) { List content = jpaQueryFactory diff --git a/src/main/java/com/projectlyrics/server/domain/artist/service/ArtistQueryService.java b/src/main/java/com/projectlyrics/server/domain/artist/service/ArtistQueryService.java index dbda1ce3..652fbd73 100644 --- a/src/main/java/com/projectlyrics/server/domain/artist/service/ArtistQueryService.java +++ b/src/main/java/com/projectlyrics/server/domain/artist/service/ArtistQueryService.java @@ -11,6 +11,8 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.util.List; + @RequiredArgsConstructor @Transactional(readOnly = true) @Service @@ -40,4 +42,8 @@ public CursorBasePaginatedResponse searchArtists(String query return CursorBasePaginatedResponse.of(searchedArtists); } + + public List getArtistsByIds(List artistIds) { + return artistQueryRepository.findAllByIds(artistIds); + } } diff --git a/src/main/java/com/projectlyrics/server/domain/favoriteartist/api/FavoriteArtistController.java b/src/main/java/com/projectlyrics/server/domain/favoriteartist/api/FavoriteArtistController.java new file mode 100644 index 00000000..fc67730e --- /dev/null +++ b/src/main/java/com/projectlyrics/server/domain/favoriteartist/api/FavoriteArtistController.java @@ -0,0 +1,31 @@ +package com.projectlyrics.server.domain.favoriteartist.api; + +import com.projectlyrics.server.domain.favoriteartist.dto.request.CreateFavoriteArtistListRequest; +import com.projectlyrics.server.domain.favoriteartist.service.FavoriteArtistCommandService; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +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 java.security.Principal; + +@RestController +@RequestMapping("/api/v1/favorite-artists") +@RequiredArgsConstructor +public class FavoriteArtistController { + + private final FavoriteArtistCommandService favoriteArtistCommandService; + + @PostMapping("/batch") + public ResponseEntity saveAll( + Principal principal, + @RequestBody @Valid CreateFavoriteArtistListRequest request + ) { + favoriteArtistCommandService.saveAll(Long.parseLong(principal.getName()), request); + return ResponseEntity.ok() + .build(); + } +} diff --git a/src/main/java/com/projectlyrics/server/domain/favoriteartist/dto/request/CreateFavoriteArtistListRequest.java b/src/main/java/com/projectlyrics/server/domain/favoriteartist/dto/request/CreateFavoriteArtistListRequest.java new file mode 100644 index 00000000..5bbaed35 --- /dev/null +++ b/src/main/java/com/projectlyrics/server/domain/favoriteartist/dto/request/CreateFavoriteArtistListRequest.java @@ -0,0 +1,11 @@ +package com.projectlyrics.server.domain.favoriteartist.dto.request; + +import jakarta.validation.constraints.NotEmpty; + +import java.util.List; + +public record CreateFavoriteArtistListRequest( + @NotEmpty + List artistIds +) { +} diff --git a/src/main/java/com/projectlyrics/server/domain/favoriteartist/entity/FavoriteArtist.java b/src/main/java/com/projectlyrics/server/domain/favoriteartist/entity/FavoriteArtist.java new file mode 100644 index 00000000..ab7a7583 --- /dev/null +++ b/src/main/java/com/projectlyrics/server/domain/favoriteartist/entity/FavoriteArtist.java @@ -0,0 +1,48 @@ +package com.projectlyrics.server.domain.favoriteartist.entity; + +import com.projectlyrics.server.domain.artist.entity.Artist; +import com.projectlyrics.server.domain.common.entity.BaseEntity; +import com.projectlyrics.server.domain.record.domain.Record; +import com.projectlyrics.server.domain.user.entity.User; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; +import lombok.AccessLevel; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import static com.projectlyrics.server.domain.common.util.DomainUtils.checkNull; + +@Getter +@Entity +@Table(name = "favorite_artists") +@EqualsAndHashCode(of = "id", callSuper = false) +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class FavoriteArtist extends BaseEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne(fetch = FetchType.LAZY) + private User user; + + @ManyToOne(fetch = FetchType.LAZY) + private Artist artist; + + private FavoriteArtist(User user, Artist artist) { + checkNull(user); + checkNull(artist); + this.user = user; + this.artist = artist; + } + + public static FavoriteArtist of(User user, Artist artist) { + return new FavoriteArtist(user, artist); + } +} diff --git a/src/main/java/com/projectlyrics/server/domain/favoriteartist/repository/FavoriteArtistCommandRepository.java b/src/main/java/com/projectlyrics/server/domain/favoriteartist/repository/FavoriteArtistCommandRepository.java new file mode 100644 index 00000000..2ec0d97a --- /dev/null +++ b/src/main/java/com/projectlyrics/server/domain/favoriteartist/repository/FavoriteArtistCommandRepository.java @@ -0,0 +1,9 @@ +package com.projectlyrics.server.domain.favoriteartist.repository; + +import com.projectlyrics.server.domain.favoriteartist.entity.FavoriteArtist; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface FavoriteArtistCommandRepository extends JpaRepository { +} diff --git a/src/main/java/com/projectlyrics/server/domain/favoriteartist/repository/FavoriteArtistQueryRepository.java b/src/main/java/com/projectlyrics/server/domain/favoriteartist/repository/FavoriteArtistQueryRepository.java new file mode 100644 index 00000000..1bc5a002 --- /dev/null +++ b/src/main/java/com/projectlyrics/server/domain/favoriteartist/repository/FavoriteArtistQueryRepository.java @@ -0,0 +1,9 @@ +package com.projectlyrics.server.domain.favoriteartist.repository; + +import com.projectlyrics.server.domain.favoriteartist.entity.FavoriteArtist; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Slice; + +public interface FavoriteArtistQueryRepository { + Slice findAllByUserId(Long userId, Long cursorId, Pageable pageable); +} diff --git a/src/main/java/com/projectlyrics/server/domain/favoriteartist/repository/impl/QueryDslFavoriteArtistQueryRepository.java b/src/main/java/com/projectlyrics/server/domain/favoriteartist/repository/impl/QueryDslFavoriteArtistQueryRepository.java new file mode 100644 index 00000000..a80215fe --- /dev/null +++ b/src/main/java/com/projectlyrics/server/domain/favoriteartist/repository/impl/QueryDslFavoriteArtistQueryRepository.java @@ -0,0 +1,40 @@ +package com.projectlyrics.server.domain.favoriteartist.repository.impl; + +import com.projectlyrics.server.domain.artist.entity.QArtist; +import com.projectlyrics.server.domain.common.util.QueryDslUtils; +import com.projectlyrics.server.domain.favoriteartist.entity.FavoriteArtist; +import com.projectlyrics.server.domain.favoriteartist.entity.QFavoriteArtist; +import com.projectlyrics.server.domain.favoriteartist.repository.FavoriteArtistQueryRepository; +import com.projectlyrics.server.domain.user.entity.QUser; +import com.querydsl.jpa.impl.JPAQueryFactory; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Slice; +import org.springframework.data.domain.SliceImpl; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +@RequiredArgsConstructor +public class QueryDslFavoriteArtistQueryRepository implements FavoriteArtistQueryRepository { + + private final JPAQueryFactory jpaQueryFactory; + + @Override + public Slice findAllByUserId(Long userId, Long cursorId, Pageable pageable) { + List content = jpaQueryFactory.selectFrom(QFavoriteArtist.favoriteArtist) + .where( + QFavoriteArtist.favoriteArtist.user.id.eq(userId), + QFavoriteArtist.favoriteArtist.deletedAt.isNull(), + QueryDslUtils.gtCursorId(cursorId, QFavoriteArtist.favoriteArtist.id) + ) + .leftJoin(QFavoriteArtist.favoriteArtist.user, QUser.user) + .fetchJoin() + .leftJoin(QFavoriteArtist.favoriteArtist.artist, QArtist.artist) + .fetchJoin() + .limit(pageable.getPageSize() + 1) + .fetch(); + return new SliceImpl<>(content, pageable, QueryDslUtils.checkIfHasNext(pageable, content)); + } +} diff --git a/src/main/java/com/projectlyrics/server/domain/favoriteartist/service/FavoriteArtistCommandService.java b/src/main/java/com/projectlyrics/server/domain/favoriteartist/service/FavoriteArtistCommandService.java new file mode 100644 index 00000000..17986aaf --- /dev/null +++ b/src/main/java/com/projectlyrics/server/domain/favoriteartist/service/FavoriteArtistCommandService.java @@ -0,0 +1,33 @@ +package com.projectlyrics.server.domain.favoriteartist.service; + +import com.projectlyrics.server.domain.artist.entity.Artist; +import com.projectlyrics.server.domain.artist.service.ArtistQueryService; +import com.projectlyrics.server.domain.favoriteartist.dto.request.CreateFavoriteArtistListRequest; +import com.projectlyrics.server.domain.favoriteartist.entity.FavoriteArtist; +import com.projectlyrics.server.domain.favoriteartist.repository.FavoriteArtistCommandRepository; +import com.projectlyrics.server.domain.user.entity.User; +import com.projectlyrics.server.domain.user.service.UserQueryService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +@Service +@Transactional +@RequiredArgsConstructor +public class FavoriteArtistCommandService { + + private final FavoriteArtistCommandRepository favoriteArtistCommandRepository; + private final UserQueryService userQueryService; + private final ArtistQueryService artistQueryService; + + public void saveAll(Long userId, CreateFavoriteArtistListRequest request) { + User user = userQueryService.getUserById(userId); + List artistList = artistQueryService.getArtistsByIds(request.artistIds()); + List favoriteArtistList = artistList.stream() + .map(artist -> FavoriteArtist.of(user, artist)) + .toList(); + favoriteArtistCommandRepository.saveAll(favoriteArtistList); + } +} From 83133cb4b69360cbbd3d932996c4fc64866a84d0 Mon Sep 17 00:00:00 2001 From: wlsh44 Date: Fri, 28 Jun 2024 16:54:01 +0900 Subject: [PATCH 05/14] =?UTF-8?q?[SCRUM-120]=20test:=20=EA=B4=80=EC=8B=AC?= =?UTF-8?q?=20=EC=95=84=ED=8B=B0=EC=8A=A4=ED=8A=B8=20=EB=93=B1=EB=A1=9D=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../server/common/ControllerTest.java | 4 ++ .../api/FavoriteArtistControllerTest.java | 32 +++++++++ .../FavoriteArtistCommandServiceTest.java | 70 +++++++++++++++++++ src/test/resources/application.yml | 1 + 4 files changed, 107 insertions(+) create mode 100644 src/test/java/com/projectlyrics/server/domain/favoriteartist/api/FavoriteArtistControllerTest.java create mode 100644 src/test/java/com/projectlyrics/server/domain/favoriteartist/service/FavoriteArtistCommandServiceTest.java diff --git a/src/test/java/com/projectlyrics/server/common/ControllerTest.java b/src/test/java/com/projectlyrics/server/common/ControllerTest.java index 69669146..42af9c46 100644 --- a/src/test/java/com/projectlyrics/server/common/ControllerTest.java +++ b/src/test/java/com/projectlyrics/server/common/ControllerTest.java @@ -10,6 +10,7 @@ import com.projectlyrics.server.domain.auth.jwt.dto.AuthToken; import com.projectlyrics.server.domain.auth.service.AuthCommandService; import com.projectlyrics.server.domain.auth.service.AuthQueryService; +import com.projectlyrics.server.domain.favoriteartist.service.FavoriteArtistCommandService; import com.projectlyrics.server.domain.user.service.UserCommandService; import com.projectlyrics.server.domain.record.service.RecordCommandService; import com.projectlyrics.server.domain.record.service.RecordQueryService; @@ -67,6 +68,9 @@ public abstract class ControllerTest { @MockBean protected AuthQueryService authQueryService; + @MockBean + protected FavoriteArtistCommandService favoriteArtistCommandService; + protected String accessToken; @BeforeEach diff --git a/src/test/java/com/projectlyrics/server/domain/favoriteartist/api/FavoriteArtistControllerTest.java b/src/test/java/com/projectlyrics/server/domain/favoriteartist/api/FavoriteArtistControllerTest.java new file mode 100644 index 00000000..428445f7 --- /dev/null +++ b/src/test/java/com/projectlyrics/server/domain/favoriteartist/api/FavoriteArtistControllerTest.java @@ -0,0 +1,32 @@ +package com.projectlyrics.server.domain.favoriteartist.api; + +import com.projectlyrics.server.common.ControllerTest; +import com.projectlyrics.server.domain.favoriteartist.dto.request.CreateFavoriteArtistListRequest; +import org.junit.jupiter.api.Test; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.security.test.context.support.WithMockUser; + +import java.util.List; + +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +class FavoriteArtistControllerTest extends ControllerTest { + + @Test + @WithMockUser + void 관심_아티스트들을_저장해야_한다() throws Exception { + //given + CreateFavoriteArtistListRequest request = new CreateFavoriteArtistListRequest(List.of(1L, 2L)); + + //when then + mockMvc.perform(post("/api/v1/favorite-artists/batch") + .with(csrf()) + .header(HttpHeaders.AUTHORIZATION, "Bearer " + accessToken) + .contentType(MediaType.APPLICATION_JSON) + .content(mapper.writeValueAsString(request))) + .andExpect(status().isOk()); + } +} diff --git a/src/test/java/com/projectlyrics/server/domain/favoriteartist/service/FavoriteArtistCommandServiceTest.java b/src/test/java/com/projectlyrics/server/domain/favoriteartist/service/FavoriteArtistCommandServiceTest.java new file mode 100644 index 00000000..7f276928 --- /dev/null +++ b/src/test/java/com/projectlyrics/server/domain/favoriteartist/service/FavoriteArtistCommandServiceTest.java @@ -0,0 +1,70 @@ +package com.projectlyrics.server.domain.favoriteartist.service; + +import com.projectlyrics.server.common.IntegrationTest; +import com.projectlyrics.server.common.fixture.ArtistFixture; +import com.projectlyrics.server.common.fixture.UserFixture; +import com.projectlyrics.server.domain.artist.entity.Artist; +import com.projectlyrics.server.domain.artist.repository.ArtistCommandRepository; +import com.projectlyrics.server.domain.favoriteartist.dto.request.CreateFavoriteArtistListRequest; +import com.projectlyrics.server.domain.favoriteartist.entity.FavoriteArtist; +import com.projectlyrics.server.domain.favoriteartist.repository.FavoriteArtistQueryRepository; +import com.projectlyrics.server.domain.record.repository.RecordCommandRepository; +import com.projectlyrics.server.domain.user.entity.User; +import com.projectlyrics.server.domain.user.repository.UserCommandRepository; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Slice; + +import java.util.List; + +import static org.assertj.core.api.SoftAssertions.assertSoftly; + +class FavoriteArtistCommandServiceTest extends IntegrationTest { + + @Autowired + UserCommandRepository userCommandRepository; + + @Autowired + FavoriteArtistCommandService sut; + + @Autowired + ArtistCommandRepository artistCommandRepository; + + @Autowired + RecordCommandRepository repository; + + @Autowired + FavoriteArtistQueryRepository favoriteArtistQueryRepository; + + @Test + void 관심_아티스트_리스트를_저장해야_한다() throws Exception { + //given + Artist artist1 = artistCommandRepository.save(ArtistFixture.createWithName("신지훈")); + Artist artist2 = artistCommandRepository.save(ArtistFixture.createWithName("너드 커넥션")); + Artist artist3 = artistCommandRepository.save(ArtistFixture.createWithName("한로로")); + CreateFavoriteArtistListRequest request = new CreateFavoriteArtistListRequest(List.of( + artist1.getId(), + artist2.getId(), + artist3.getId() + ) + ); + User user = userCommandRepository.save(UserFixture.create()); + + //when + sut.saveAll(user.getId(), request); + + //then + PageRequest pageRequest = PageRequest.ofSize(5); + Slice favoriteArtistSlice = favoriteArtistQueryRepository.findAllByUserId(user.getId(), artist1.getId(), pageRequest); + List artistList = List.of(artist1, artist2, artist3); + assertSoftly(s -> { + s.assertThat(favoriteArtistSlice.getContent()).hasSize(3); + s.assertThat(favoriteArtistSlice.hasNext()).isFalse(); + favoriteArtistSlice.getContent().forEach(favoriteArtist -> { + s.assertThat(favoriteArtist.getUser()).isEqualTo(user); + s.assertThat(artistList.contains(favoriteArtist.getArtist())).isTrue(); + }); + }); + } +} diff --git a/src/test/resources/application.yml b/src/test/resources/application.yml index a7a51d76..f9e17ef4 100644 --- a/src/test/resources/application.yml +++ b/src/test/resources/application.yml @@ -15,6 +15,7 @@ spring: dialect: org.hibernate.dialect.MySQLDialect show_sql: true format_sql: true + default_batch_fetch_size: 1000 open-in-view: false datasource: From 59273f171c74e13ed064a442cd6833e16936876c Mon Sep 17 00:00:00 2001 From: wlsh44 Date: Fri, 28 Jun 2024 17:53:42 +0900 Subject: [PATCH 06/14] =?UTF-8?q?[SCRUM-120]=20chore:=20=EA=B0=9C=EB=B0=9C?= =?UTF-8?q?=EC=9A=A9=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EC=B4=88=EA=B8=B0?= =?UTF-8?q?=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../projectlyrics/server/global/dev/DummyDataInitializer.java | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 src/main/java/com/projectlyrics/server/global/dev/DummyDataInitializer.java diff --git a/src/main/java/com/projectlyrics/server/global/dev/DummyDataInitializer.java b/src/main/java/com/projectlyrics/server/global/dev/DummyDataInitializer.java new file mode 100644 index 00000000..71731617 --- /dev/null +++ b/src/main/java/com/projectlyrics/server/global/dev/DummyDataInitializer.java @@ -0,0 +1,2 @@ +package com.projectlyrics.server.global.dev;public class DummyDataInitiailzer { +} From 4f27f4aa886f0cf4c64b5b06f19f32cb6aae212a Mon Sep 17 00:00:00 2001 From: wlsh44 Date: Fri, 28 Jun 2024 17:54:07 +0900 Subject: [PATCH 07/14] =?UTF-8?q?[SCRUM-120]=20fix:=20requestParam=20?= =?UTF-8?q?=EC=9D=B4=EB=A6=84=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../server/domain/artist/api/ArtistController.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/projectlyrics/server/domain/artist/api/ArtistController.java b/src/main/java/com/projectlyrics/server/domain/artist/api/ArtistController.java index a87d526c..1d346280 100644 --- a/src/main/java/com/projectlyrics/server/domain/artist/api/ArtistController.java +++ b/src/main/java/com/projectlyrics/server/domain/artist/api/ArtistController.java @@ -72,18 +72,18 @@ public ResponseEntity getArtist( @GetMapping public ResponseEntity> getArtistList( - @RequestParam(required = false) Long cursor, - @RequestParam(defaultValue = "10") int size + @RequestParam(name = "cursor", required = false) Long cursor, + @RequestParam(name = "size", defaultValue = "10") int size ) { return ResponseEntity .status(HttpStatus.OK) - .body(artistQueryService.getArtistList(cursor, PageRequest.of(0, size))); + .body(artistQueryService.getArtistList(cursor, PageRequest.ofSize(size))); } @GetMapping("/search") public ResponseEntity> searchArtist( - @RequestParam(required = false) Long cursor, - @RequestParam(required = false, defaultValue = "5") int size, + @RequestParam(name = "cursor", required = false) Long cursor, + @RequestParam(name = "size", defaultValue = "10") int size, @RequestParam String query ) { return ResponseEntity From ed7e8a2f63364167364a25ff88b254a07d56a1da Mon Sep 17 00:00:00 2001 From: wlsh44 Date: Fri, 28 Jun 2024 17:55:40 +0900 Subject: [PATCH 08/14] =?UTF-8?q?[SCRUM-120]=20chore:=20=EA=B0=9C=EB=B0=9C?= =?UTF-8?q?=EC=9A=A9=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EC=B4=88=EA=B8=B0?= =?UTF-8?q?=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/dev/DummyDataInitializer.java | 54 ++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/projectlyrics/server/global/dev/DummyDataInitializer.java b/src/main/java/com/projectlyrics/server/global/dev/DummyDataInitializer.java index 71731617..70c8798d 100644 --- a/src/main/java/com/projectlyrics/server/global/dev/DummyDataInitializer.java +++ b/src/main/java/com/projectlyrics/server/global/dev/DummyDataInitializer.java @@ -1,2 +1,54 @@ -package com.projectlyrics.server.global.dev;public class DummyDataInitiailzer { +package com.projectlyrics.server.global.dev; + +import com.projectlyrics.server.domain.artist.entity.Artist; +import com.projectlyrics.server.domain.artist.repository.ArtistCommandRepository; +import com.projectlyrics.server.domain.auth.entity.Auth; +import com.projectlyrics.server.domain.auth.entity.enumerate.AuthProvider; +import com.projectlyrics.server.domain.auth.entity.enumerate.Role; +import com.projectlyrics.server.domain.auth.jwt.JwtTokenProvider; +import com.projectlyrics.server.domain.auth.jwt.dto.AuthToken; +import com.projectlyrics.server.domain.user.entity.Gender; +import com.projectlyrics.server.domain.user.entity.TermsAgreements; +import com.projectlyrics.server.domain.user.entity.User; +import com.projectlyrics.server.domain.user.repository.UserCommandRepository; +import jakarta.annotation.PostConstruct; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.annotation.Profile; +import org.springframework.stereotype.Component; + +import java.util.List; + +@Slf4j +@Profile({"dev", "local"}) +@Component +@RequiredArgsConstructor +public class DummyDataInitializer { + + private final UserCommandRepository userCommandRepository; + private final ArtistCommandRepository artistCommandRepository; + private final JwtTokenProvider tokenProvider; + + @PostConstruct + public void init() { + artistCommandRepository.save(Artist.of("너드 커넥션", null)); + artistCommandRepository.save(Artist.of("한로로", null)); + artistCommandRepository.save(Artist.of("신지훈", null)); + artistCommandRepository.save(Artist.of("하현상", null)); + artistCommandRepository.save(Artist.of("잔나비", null)); + artistCommandRepository.save(Artist.of("아이유", null)); + artistCommandRepository.save(Artist.of("ed sheeran", null)); + artistCommandRepository.save(Artist.of("10cm", null)); + artistCommandRepository.save(Artist.of("oasis", null)); + artistCommandRepository.save(Artist.of("국카스텐", null)); + artistCommandRepository.save(Artist.of("artist1", null)); + artistCommandRepository.save(Artist.of("artist2", null)); + artistCommandRepository.save(Artist.of("artist3", null)); + artistCommandRepository.save(Artist.of("artist4", null)); + artistCommandRepository.save(Artist.of("artist5", null)); + User user = userCommandRepository.save(User.of("test@test.com", Auth.of(AuthProvider.KAKAO, Role.USER, "socialId"), "test1", Gender.MALE, 1999, List.of(new TermsAgreements(true, "약관1", "약관 내용")))); + AuthToken authToken = tokenProvider.issueTokens(user.getId()); + log.info("accessToken: {}", authToken.accessToken()); + log.info("refreshToken: {}", authToken.refreshToken()); + } } From bbea6ea2878b3619f78a8640987bca5934ae459c Mon Sep 17 00:00:00 2001 From: wlsh44 Date: Fri, 28 Jun 2024 17:56:10 +0900 Subject: [PATCH 09/14] =?UTF-8?q?[SCRUM-120]=20refactor:=20dto=20=ED=95=84?= =?UTF-8?q?=EB=93=9C=20=EC=9D=B4=EB=A6=84=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../server/domain/artist/api/ArtistControllerSwagger.java | 2 +- .../server/domain/artist/dto/response/ArtistGetResponse.java | 2 +- .../domain/common/dto/util/CursorBasePaginatedResponse.java | 2 +- .../server/domain/artist/service/ArtistQueryServiceTest.java | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/projectlyrics/server/domain/artist/api/ArtistControllerSwagger.java b/src/main/java/com/projectlyrics/server/domain/artist/api/ArtistControllerSwagger.java index 914ba52a..54bf2977 100644 --- a/src/main/java/com/projectlyrics/server/domain/artist/api/ArtistControllerSwagger.java +++ b/src/main/java/com/projectlyrics/server/domain/artist/api/ArtistControllerSwagger.java @@ -55,7 +55,7 @@ public ResponseEntity getArtist( description = "아티스트의 데이터의 목록을 조회합니다." ) public ResponseEntity> getArtistList( - @Parameter(description = "이전에 응답 받은 cursor 값. 응답 받은 값이 없다면 해당 값을 비워서 요청합니다.") + @Parameter(description = "이전에 응답 받은 nextCursor 값. 응답 받은 값이 없다면 해당 값을 비워서 요청합니다.") @RequestParam(required = false) Long cursor, @Parameter(description = "조회할 데이터의 최대 크기") @RequestParam(defaultValue = "10") int size diff --git a/src/main/java/com/projectlyrics/server/domain/artist/dto/response/ArtistGetResponse.java b/src/main/java/com/projectlyrics/server/domain/artist/dto/response/ArtistGetResponse.java index 290810b0..1b0a9926 100644 --- a/src/main/java/com/projectlyrics/server/domain/artist/dto/response/ArtistGetResponse.java +++ b/src/main/java/com/projectlyrics/server/domain/artist/dto/response/ArtistGetResponse.java @@ -6,7 +6,7 @@ public record ArtistGetResponse( Long id, String name, - String profileImageCdnLink + String imageUrl ) implements CursorResponse { public static ArtistGetResponse of(Artist artist) { diff --git a/src/main/java/com/projectlyrics/server/domain/common/dto/util/CursorBasePaginatedResponse.java b/src/main/java/com/projectlyrics/server/domain/common/dto/util/CursorBasePaginatedResponse.java index 6938c537..7f837845 100644 --- a/src/main/java/com/projectlyrics/server/domain/common/dto/util/CursorBasePaginatedResponse.java +++ b/src/main/java/com/projectlyrics/server/domain/common/dto/util/CursorBasePaginatedResponse.java @@ -7,7 +7,7 @@ import org.springframework.data.domain.Slice; public record CursorBasePaginatedResponse( - @Schema(description = "다음 페이지를 요청하기 위한 커서 값") Long cursor, + @Schema(description = "다음 페이지를 요청하기 위한 커서 값") Long nextCursor, @Schema(description = "다음 페이지 존재 여부") boolean hasNext, @Schema(description = "요청한 데이터") List data ) { diff --git a/src/test/java/com/projectlyrics/server/domain/artist/service/ArtistQueryServiceTest.java b/src/test/java/com/projectlyrics/server/domain/artist/service/ArtistQueryServiceTest.java index a641852b..2a8917d1 100644 --- a/src/test/java/com/projectlyrics/server/domain/artist/service/ArtistQueryServiceTest.java +++ b/src/test/java/com/projectlyrics/server/domain/artist/service/ArtistQueryServiceTest.java @@ -50,7 +50,7 @@ class ArtistQueryServiceTest { then(artistQueryRepository).should().findByIdAndNotDeleted(anyLong()); assertThat(getArtistResponse.id()).isEqualTo(artistId); assertThat(getArtistResponse.name()).isEqualTo(artist.getName()); - assertThat(getArtistResponse.profileImageCdnLink()).isEqualTo(artist.getImageUrl()); + assertThat(getArtistResponse.imageUrl()).isEqualTo(artist.getImageUrl()); } @Test From 725a6a593d035d784fa494d2cec5a32396088a2a Mon Sep 17 00:00:00 2001 From: wlsh44 Date: Sun, 30 Jun 2024 17:14:16 +0900 Subject: [PATCH 10/14] =?UTF-8?q?[SCRUM-120]=20=08fix:=20=EC=9D=B8?= =?UTF-8?q?=EC=A6=9D=20=EC=98=88=EC=99=B8=EC=B2=98=EB=A6=AC=20=EB=B0=8F=20?= =?UTF-8?q?=20=ED=86=A0=ED=81=B0=20=EA=B2=80=EC=A6=9D=20=EB=B2=84=EA=B7=B8?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../authentication/JwtAuthenticationFilter.java | 15 +++++---------- .../server/domain/auth/jwt/JwtTokenProvider.java | 4 ++-- .../global/handler/GlobalExceptionHandler.java | 8 ++++++++ 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/main/java/com/projectlyrics/server/domain/auth/authentication/JwtAuthenticationFilter.java b/src/main/java/com/projectlyrics/server/domain/auth/authentication/JwtAuthenticationFilter.java index 830d3b26..c1b6435f 100644 --- a/src/main/java/com/projectlyrics/server/domain/auth/authentication/JwtAuthenticationFilter.java +++ b/src/main/java/com/projectlyrics/server/domain/auth/authentication/JwtAuthenticationFilter.java @@ -36,16 +36,11 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { - try { - String token = getAccessTokenFromRequest(request); - - validateToken(token); - setUserIntoContext(token, request); - } catch (RuntimeException e) { - log.debug(e.getMessage()); - } finally { - filterChain.doFilter(request, response); - } + String token = getAccessTokenFromRequest(request); + + validateToken(token); + setUserIntoContext(token, request); + filterChain.doFilter(request, response); } private String getAccessTokenFromRequest(HttpServletRequest request) { diff --git a/src/main/java/com/projectlyrics/server/domain/auth/jwt/JwtTokenProvider.java b/src/main/java/com/projectlyrics/server/domain/auth/jwt/JwtTokenProvider.java index cec489fd..46b7d27a 100644 --- a/src/main/java/com/projectlyrics/server/domain/auth/jwt/JwtTokenProvider.java +++ b/src/main/java/com/projectlyrics/server/domain/auth/jwt/JwtTokenProvider.java @@ -82,14 +82,14 @@ public JwtValidationType validateToken(String token) { Claims claims = getBody(token); return VALID_JWT; - } catch (MalformedJwtException ex) { - return INVALID_JWT_TOKEN; } catch (ExpiredJwtException ex) { return EXPIRED_JWT_TOKEN; } catch (UnsupportedJwtException ex) { return UNSUPPORTED_JWT_TOKEN; } catch (IllegalArgumentException ex) { return EMPTY_JWT; + } catch (Exception ex) { + return INVALID_JWT_TOKEN; } } diff --git a/src/main/java/com/projectlyrics/server/global/handler/GlobalExceptionHandler.java b/src/main/java/com/projectlyrics/server/global/handler/GlobalExceptionHandler.java index 39d385f2..73894949 100644 --- a/src/main/java/com/projectlyrics/server/global/handler/GlobalExceptionHandler.java +++ b/src/main/java/com/projectlyrics/server/global/handler/GlobalExceptionHandler.java @@ -10,6 +10,7 @@ import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.servlet.resource.NoResourceFoundException; @Slf4j @RestControllerAdvice @@ -36,6 +37,13 @@ public ResponseEntity handleFeelinException(FeelinException e) { .body(ErrorResponse.of(e.getErrorCode())); } + @ExceptionHandler(NoResourceFoundException.class) + public ResponseEntity handleNoResourceFoundException(NoResourceFoundException e) { + return ResponseEntity + .status(HttpStatus.NOT_FOUND) + .body(ErrorResponse.of(ErrorCode.NOT_FOUND)); + } + @ExceptionHandler(Exception.class) public ResponseEntity handleException(Exception e) { log.error("", e); From 7768581d6b23e6f88fd2d2067a6bd06670d3b4dc Mon Sep 17 00:00:00 2001 From: wlsh44 Date: Sun, 30 Jun 2024 17:15:29 +0900 Subject: [PATCH 11/14] =?UTF-8?q?[SCRUM-120]=20fix:=20=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=95=20=EC=B2=98=EB=A6=AC=20=EA=B4=80=EB=A0=A8=20=EB=B2=84?= =?UTF-8?q?=EA=B7=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/impl/QueryDslArtistQueryRepository.java | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/projectlyrics/server/domain/artist/repository/impl/QueryDslArtistQueryRepository.java b/src/main/java/com/projectlyrics/server/domain/artist/repository/impl/QueryDslArtistQueryRepository.java index c27d4907..859fdc7f 100644 --- a/src/main/java/com/projectlyrics/server/domain/artist/repository/impl/QueryDslArtistQueryRepository.java +++ b/src/main/java/com/projectlyrics/server/domain/artist/repository/impl/QueryDslArtistQueryRepository.java @@ -41,7 +41,7 @@ public Slice findAllAndNotDeleted(Long cursor, Pageable pageable) { List content = jpaQueryFactory .selectFrom(QArtist.artist) .where( - goeCursorId(cursor), + QueryDslUtils.gtCursorId(cursor, QArtist.artist.id), QArtist.artist.deletedAt.isNull() ) .limit(pageable.getPageSize() + 1) @@ -65,7 +65,7 @@ public Slice findAllByQueryAndNotDeleted(String query, Long cursor, Page List content = jpaQueryFactory .selectFrom(QArtist.artist) .where( - goeCursorId(cursor), + QueryDslUtils.gtCursorId(cursor, QArtist.artist.id), QArtist.artist.deletedAt.isNull(), anyOf( QArtist.artist.name.contains(query) @@ -76,8 +76,4 @@ public Slice findAllByQueryAndNotDeleted(String query, Long cursor, Page return new SliceImpl<>(content, pageable, QueryDslUtils.checkIfHasNext(pageable, content)); } - - private BooleanExpression goeCursorId(Long cursor) { - return cursor == null ? null : QArtist.artist.id.goe(cursor); - } } From 1b4e3ff41cb89b038f7ee1f795faffe9aa88066c Mon Sep 17 00:00:00 2001 From: wlsh44 Date: Sun, 30 Jun 2024 22:29:50 +0900 Subject: [PATCH 12/14] =?UTF-8?q?[SCRUM-120]=20fix:=20=ED=97=88=EC=9A=A9?= =?UTF-8?q?=20api=20=EC=B2=98=EB=A6=AC=20=EB=B0=A9=EC=8B=9D=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/authentication/JwtAuthenticationFilter.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/main/java/com/projectlyrics/server/domain/auth/authentication/JwtAuthenticationFilter.java b/src/main/java/com/projectlyrics/server/domain/auth/authentication/JwtAuthenticationFilter.java index c1b6435f..a80ab292 100644 --- a/src/main/java/com/projectlyrics/server/domain/auth/authentication/JwtAuthenticationFilter.java +++ b/src/main/java/com/projectlyrics/server/domain/auth/authentication/JwtAuthenticationFilter.java @@ -15,10 +15,12 @@ import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; +import java.util.Arrays; import java.util.Optional; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; import org.springframework.stereotype.Component; @@ -31,14 +33,24 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter { private static final String TOKEN_PREFIX = "Bearer "; + @Value("#{'${auth.free-apis}'.split(',')}") + private String[] excludePath; + private final JwtTokenProvider jwtTokenProvider; + @Override + protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException { + String path = request.getRequestURI(); + return Arrays.asList(excludePath).contains(path); + } + @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { String token = getAccessTokenFromRequest(request); validateToken(token); + setUserIntoContext(token, request); filterChain.doFilter(request, response); } From e30e937f7e6f6aa7399c3b8dff0c8ab2dd656f1c Mon Sep 17 00:00:00 2001 From: wlsh44 Date: Sun, 30 Jun 2024 22:30:09 +0900 Subject: [PATCH 13/14] =?UTF-8?q?[SCRUM-120]=20test:=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EA=B9=A8=EC=A7=80=EB=8A=94=20=EB=B6=80=EB=B6=84=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../artist/dto/request/ArtistAddRequestTest.java | 16 +--------------- .../FavoriteArtistCommandServiceTest.java | 2 +- 2 files changed, 2 insertions(+), 16 deletions(-) diff --git a/src/test/java/com/projectlyrics/server/domain/artist/dto/request/ArtistAddRequestTest.java b/src/test/java/com/projectlyrics/server/domain/artist/dto/request/ArtistAddRequestTest.java index 8d851a89..88d8fd7c 100644 --- a/src/test/java/com/projectlyrics/server/domain/artist/dto/request/ArtistAddRequestTest.java +++ b/src/test/java/com/projectlyrics/server/domain/artist/dto/request/ArtistAddRequestTest.java @@ -26,21 +26,7 @@ class ArtistAddRequestTest { // then violation.forEach(error -> { - assertThat(error.getMessage()).isEqualTo("빈 문자열 또는 공백 문자열은 허용하지 않습니다."); - }); - } - - @Test - void 아티스트의_이미지_경로가_https로_시작하지_않는_문자열이라면_validation_에러가_발생한다() { - // given - ArtistAddRequest addArtistRequest = createAddArtistRequest("넬", "imageUrl"); - - // when - Set> violation = validator.validate(addArtistRequest); - - // then - violation.forEach(error -> { - assertThat(error.getMessage()).isEqualTo("이미지 경로는 https://로 시작해야 합니다."); + assertThat(error.getMessage()).isEqualTo("공백일 수 없습니다"); }); } diff --git a/src/test/java/com/projectlyrics/server/domain/favoriteartist/service/FavoriteArtistCommandServiceTest.java b/src/test/java/com/projectlyrics/server/domain/favoriteartist/service/FavoriteArtistCommandServiceTest.java index 7f276928..ba330398 100644 --- a/src/test/java/com/projectlyrics/server/domain/favoriteartist/service/FavoriteArtistCommandServiceTest.java +++ b/src/test/java/com/projectlyrics/server/domain/favoriteartist/service/FavoriteArtistCommandServiceTest.java @@ -56,7 +56,7 @@ class FavoriteArtistCommandServiceTest extends IntegrationTest { //then PageRequest pageRequest = PageRequest.ofSize(5); - Slice favoriteArtistSlice = favoriteArtistQueryRepository.findAllByUserId(user.getId(), artist1.getId(), pageRequest); + Slice favoriteArtistSlice = favoriteArtistQueryRepository.findAllByUserId(user.getId(), null, pageRequest); List artistList = List.of(artist1, artist2, artist3); assertSoftly(s -> { s.assertThat(favoriteArtistSlice.getContent()).hasSize(3); From b7a823e5c4f0b089a06050fa00892c832693cf97 Mon Sep 17 00:00:00 2001 From: wlsh44 Date: Mon, 1 Jul 2024 17:37:55 +0900 Subject: [PATCH 14/14] =?UTF-8?q?[SCRUM-120]=20test:=20dto=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/request/ArtistAddRequestTest.java | 36 ------------------- 1 file changed, 36 deletions(-) delete mode 100644 src/test/java/com/projectlyrics/server/domain/artist/dto/request/ArtistAddRequestTest.java diff --git a/src/test/java/com/projectlyrics/server/domain/artist/dto/request/ArtistAddRequestTest.java b/src/test/java/com/projectlyrics/server/domain/artist/dto/request/ArtistAddRequestTest.java deleted file mode 100644 index 88d8fd7c..00000000 --- a/src/test/java/com/projectlyrics/server/domain/artist/dto/request/ArtistAddRequestTest.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.projectlyrics.server.domain.artist.dto.request; - -import jakarta.validation.ConstraintViolation; -import jakarta.validation.Validation; -import jakarta.validation.Validator; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.ValueSource; - -import java.util.Set; - -import static org.assertj.core.api.Assertions.assertThat; - -class ArtistAddRequestTest { - - private static Validator validator = Validation.buildDefaultValidatorFactory().getValidator(); - - @ParameterizedTest - @ValueSource(strings = {"", " "}) - void 아티스트의_이름이_빈_문자열이나_공백_문자열이라면_validation_에러가_발생한다(String name) { - // given - ArtistAddRequest addArtistRequest = createAddArtistRequest(name, "https://~"); - - // when - Set> violation = validator.validate(addArtistRequest); - - // then - violation.forEach(error -> { - assertThat(error.getMessage()).isEqualTo("공백일 수 없습니다"); - }); - } - - private ArtistAddRequest createAddArtistRequest(String name, String profileImageCdnLink) { - return new ArtistAddRequest(name, profileImageCdnLink); - } -} \ No newline at end of file