-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
* feat: 캐러셀용 최신 노래 조회 API 구현 * fix: voting_song_part 테이블 삭제 스키마 추가 * feat: 캐러셀용 최신 노래 조회 API 구현 * fix: voting_song_part 테이블 삭제 스키마 추가 * fix: 킬링파트 길이 수정 * feat: 캐러셀용 최신 노래 조회 API 구현 * fix: voting_song_part 테이블 삭제 스키마 추가 * feat: 캐러셀용 최신 노래 조회 API 구현 * fix: 킬링파트 길이 수정 * fix: Artist 엔티티 병합, 실패하는 테스트 수정 --------- Co-authored-by: 스플릿 <[email protected]>
- Loading branch information
1 parent
fced3bd
commit fa8be3c
Showing
9 changed files
with
265 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
37 changes: 37 additions & 0 deletions
37
backend/src/main/java/shook/shook/song/application/dto/RecentSongCarouselResponse.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package shook.shook.song.application.dto; | ||
|
||
import io.swagger.v3.oas.annotations.media.Schema; | ||
import lombok.AllArgsConstructor; | ||
import lombok.Getter; | ||
import shook.shook.song.domain.Song; | ||
|
||
@Schema(description = "캐러셀에 보여질 최근 노래 응답") | ||
@AllArgsConstructor | ||
@Getter | ||
public class RecentSongCarouselResponse { | ||
|
||
@Schema(description = "노래 id", example = "1") | ||
private final Long id; | ||
|
||
@Schema(description = "노래 제목", example = "노래제목") | ||
private final String title; | ||
|
||
@Schema(description = "가수 이름", example = "가수") | ||
private final String singer; | ||
|
||
@Schema(description = "비디오 영상 길이", example = "274") | ||
private final int videoLength; | ||
|
||
@Schema(description = "앨범 자켓 이미지 url", example = "https://image.com/album_cover.jpg") | ||
private final String albumCoverUrl; | ||
|
||
public static RecentSongCarouselResponse from(final Song song) { | ||
return new RecentSongCarouselResponse( | ||
song.getId(), | ||
song.getTitle(), | ||
song.getArtistName(), | ||
song.getLength(), | ||
song.getAlbumCoverUrl() | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
29 changes: 29 additions & 0 deletions
29
backend/src/main/java/shook/shook/song/ui/CarouselSongController.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
package shook.shook.song.ui; | ||
|
||
import java.util.List; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.web.bind.annotation.GetMapping; | ||
import org.springframework.web.bind.annotation.RequestMapping; | ||
import org.springframework.web.bind.annotation.RequestParam; | ||
import org.springframework.web.bind.annotation.RestController; | ||
import shook.shook.song.application.SongService; | ||
import shook.shook.song.application.dto.RecentSongCarouselResponse; | ||
import shook.shook.song.ui.openapi.CarouselSongApi; | ||
|
||
@RequiredArgsConstructor | ||
@RequestMapping("/songs/recent") | ||
@RestController | ||
public class CarouselSongController implements CarouselSongApi { | ||
|
||
private final SongService songService; | ||
|
||
@GetMapping | ||
public ResponseEntity<List<RecentSongCarouselResponse>> findRecentSongsForCarousel( | ||
@RequestParam(name = "size", defaultValue = "5", required = false) final Integer size | ||
) { | ||
final List<RecentSongCarouselResponse> responses = songService.findRecentRegisteredSongsForCarousel(size); | ||
|
||
return ResponseEntity.ok(responses); | ||
} | ||
} |
33 changes: 33 additions & 0 deletions
33
backend/src/main/java/shook/shook/song/ui/openapi/CarouselSongApi.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
package shook.shook.song.ui.openapi; | ||
|
||
import io.swagger.v3.oas.annotations.Operation; | ||
import io.swagger.v3.oas.annotations.Parameter; | ||
import io.swagger.v3.oas.annotations.responses.ApiResponse; | ||
import io.swagger.v3.oas.annotations.tags.Tag; | ||
import java.util.List; | ||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.web.bind.annotation.GetMapping; | ||
import org.springframework.web.bind.annotation.RequestParam; | ||
import shook.shook.song.application.dto.RecentSongCarouselResponse; | ||
|
||
@Tag(name = "Carousel Songs", description = "메인페이지 캐러셀 조회 API") | ||
public interface CarouselSongApi { | ||
|
||
@Operation( | ||
summary = "캐러셀에 들어갈 노래 반환", | ||
description = "캐러셀에 들어갈 노래 5개를 등록 최신 순 리스트로 반환한다." | ||
) | ||
@ApiResponse( | ||
responseCode = "200", | ||
description = "최근에 등록된 노래 리스트 조회 성공" | ||
) | ||
@Parameter( | ||
name = "size", | ||
description = "조회할 개수", | ||
example = "4" | ||
) | ||
@GetMapping | ||
ResponseEntity<List<RecentSongCarouselResponse>> findRecentSongsForCarousel( | ||
@RequestParam(name = "size", defaultValue = "5", required = false) final Integer size | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
108 changes: 108 additions & 0 deletions
108
backend/src/test/java/shook/shook/song/ui/CarouselSongControllerTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
package shook.shook.song.ui; | ||
|
||
import static org.assertj.core.api.Assertions.assertThat; | ||
|
||
import io.restassured.RestAssured; | ||
import java.util.List; | ||
import org.junit.jupiter.api.BeforeEach; | ||
import org.junit.jupiter.api.DisplayName; | ||
import org.junit.jupiter.api.Test; | ||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.boot.test.context.SpringBootTest; | ||
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; | ||
import org.springframework.boot.test.web.server.LocalServerPort; | ||
import org.springframework.http.HttpStatus; | ||
import org.springframework.test.context.jdbc.Sql; | ||
import shook.shook.song.application.dto.RecentSongCarouselResponse; | ||
import shook.shook.song.domain.Artist; | ||
import shook.shook.song.domain.Genre; | ||
import shook.shook.song.domain.KillingParts; | ||
import shook.shook.song.domain.Song; | ||
import shook.shook.song.domain.killingpart.KillingPart; | ||
import shook.shook.song.domain.repository.ArtistRepository; | ||
import shook.shook.song.domain.repository.SongRepository; | ||
|
||
@Sql("classpath:/killingpart/initialize_killing_part_song.sql") | ||
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) | ||
class CarouselSongControllerTest { | ||
|
||
@LocalServerPort | ||
private int port; | ||
|
||
@Autowired | ||
private SongRepository songRepository; | ||
|
||
@Autowired | ||
private ArtistRepository artistRepository; | ||
|
||
@BeforeEach | ||
void setUp() { | ||
RestAssured.port = port; | ||
} | ||
|
||
@DisplayName("캐러셀에 보여질 노래들을 조회하면 200 상태코드와 id 높은 순 노래 데이터가 반환된다.") | ||
@Test | ||
void findRecentSongsForCarousel() { | ||
// given | ||
songRepository.findById(3L).get(); | ||
songRepository.findById(4L).get(); | ||
songRepository.save(createNewSongWithKillingParts()); | ||
songRepository.save(createNewSongWithKillingParts()); | ||
|
||
// when | ||
final List<RecentSongCarouselResponse> response = RestAssured.given().log().all() | ||
.param("size", 4) | ||
.when().log().all() | ||
.get("/songs/recent") | ||
.then().log().all() | ||
.statusCode(HttpStatus.OK.value()) | ||
.extract() | ||
.body().jsonPath().getList(".", RecentSongCarouselResponse.class); | ||
|
||
// then | ||
assertThat(response).hasSize(4); | ||
assertThat(response.stream() | ||
.map(RecentSongCarouselResponse::getId) | ||
.toList()) | ||
.containsExactly(6L, 5L, 4L, 3L); | ||
} | ||
|
||
@DisplayName("캐러셀에 보여질 노래들을 조회할 때, size 파라미터가 전달되지 않으면 기본값인 5개가 조회된다.") | ||
@Test | ||
void findRecentSongsForCarousel_noParam() { | ||
// given | ||
songRepository.findById(3L).get(); | ||
songRepository.findById(4L).get(); | ||
songRepository.save(createNewSongWithKillingParts()); | ||
songRepository.save(createNewSongWithKillingParts()); | ||
songRepository.save(createNewSongWithKillingParts()); | ||
|
||
// when | ||
final List<RecentSongCarouselResponse> response = RestAssured.given().log().all() | ||
.when().log().all() | ||
.get("/songs/recent") | ||
.then().log().all() | ||
.statusCode(HttpStatus.OK.value()) | ||
.extract() | ||
.body().jsonPath().getList(".", RecentSongCarouselResponse.class); | ||
|
||
// then | ||
assertThat(response).hasSize(5); | ||
assertThat(response.stream() | ||
.map(RecentSongCarouselResponse::getId) | ||
.toList()) | ||
.containsExactly(7L, 6L, 5L, 4L, 3L); | ||
} | ||
|
||
private Song createNewSongWithKillingParts() { | ||
final KillingPart firstKillingPart = KillingPart.forSave(10, 5); | ||
final KillingPart secondKillingPart = KillingPart.forSave(15, 5); | ||
final KillingPart thirdKillingPart = KillingPart.forSave(20, 5); | ||
|
||
final Artist artist = new Artist("image", "name"); | ||
artistRepository.save(artist); | ||
return new Song( | ||
"제목", "비디오ID는 11글자", "이미지URL", artist, 5, Genre.from("댄스"), | ||
new KillingParts(List.of(firstKillingPart, secondKillingPart, thirdKillingPart))); | ||
} | ||
} |