Skip to content

Commit

Permalink
[BE] feat: AdminStageV1Controller에 공연 단건 조회 기능 추가 (#855) (#856)
Browse files Browse the repository at this point in the history
* feat: 공연의 식별자로 공연 단건 조회 기능 추가

* feat: 공연 단건 조회 API 추가

* test: 잘못된 path variable 수정

* chore: 공연 조회 메서드 이름 변경

- findStageById -> findById

* fix: `@Nested` 붙은 클래스 명 이름 단축

- 너무 긴 이름으로 인해 "Could not write XML test results for ..." 에러 수정

* style: 공백 제거
  • Loading branch information
seokjin8678 authored Apr 14, 2024
1 parent e733da9 commit 5071418
Show file tree
Hide file tree
Showing 5 changed files with 263 additions and 101 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import com.festago.admin.dto.stage.AdminStageV1Response;
import com.festago.admin.repository.AdminStageV1QueryDslRepository;
import com.festago.common.exception.ErrorCode;
import com.festago.common.exception.NotFoundException;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
Expand All @@ -17,4 +19,9 @@ public class AdminStageV1QueryService {
public List<AdminStageV1Response> findAllByFestivalId(Long festivalId) {
return adminStageV1QueryDslRepository.findAllByFestivalId(festivalId);
}

public AdminStageV1Response findById(Long stageId) {
return adminStageV1QueryDslRepository.findById(stageId)
.orElseThrow(() -> new NotFoundException(ErrorCode.STAGE_NOT_FOUND));
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.festago.admin.presentation.v1;

import com.festago.admin.application.AdminStageV1QueryService;
import com.festago.admin.dto.stage.AdminStageV1Response;
import com.festago.admin.dto.stage.StageV1CreateRequest;
import com.festago.admin.dto.stage.StageV1UpdateRequest;
import com.festago.stage.application.command.StageCommandFacadeService;
Expand All @@ -9,6 +11,7 @@
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
Expand All @@ -23,6 +26,15 @@
public class AdminStageV1Controller {

private final StageCommandFacadeService stageCommandFacadeService;
private final AdminStageV1QueryService adminStageV1QueryService;

@GetMapping("/{stageId}")
public ResponseEntity<AdminStageV1Response> findById(
@PathVariable Long stageId
) {
return ResponseEntity.ok()
.body(adminStageV1QueryService.findById(stageId));
}

@PostMapping
public ResponseEntity<Void> createStage(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import com.festago.common.querydsl.QueryDslRepositorySupport;
import com.festago.stage.domain.Stage;
import java.util.List;
import java.util.Optional;
import org.springframework.stereotype.Repository;

@Repository
Expand Down Expand Up @@ -43,4 +44,30 @@ public List<AdminStageV1Response> findAllByFestivalId(Long festivalId) {
)
);
}

public Optional<AdminStageV1Response> findById(Long stageId) {
List<AdminStageV1Response> response = selectFrom(stage)
.leftJoin(stageArtist).on(stageArtist.stageId.eq(stageId))
.leftJoin(artist).on(artist.id.eq(stageArtist.artistId))
.where(stage.id.eq(stageId))
.transform(
groupBy(stage.id).list(
new QAdminStageV1Response(
stage.id,
stage.startTime,
stage.ticketOpenTime,
list(new QAdminStageArtistV1Response(
artist.id,
artist.name
)),
stage.createdAt,
stage.updatedAt
)
)
);
if (response.isEmpty()) {
return Optional.empty();
}
return Optional.of(response.get(0));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,16 @@

import static java.util.stream.Collectors.toMap;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.assertj.core.api.SoftAssertions.assertSoftly;

import com.festago.admin.application.AdminStageV1QueryService;
import com.festago.admin.dto.stage.AdminStageArtistV1Response;
import com.festago.admin.dto.stage.AdminStageV1Response;
import com.festago.artist.domain.Artist;
import com.festago.artist.repository.ArtistRepository;
import com.festago.common.exception.ErrorCode;
import com.festago.common.exception.NotFoundException;
import com.festago.festival.domain.Festival;
import com.festago.festival.repository.FestivalRepository;
import com.festago.school.repository.SchoolRepository;
Expand Down Expand Up @@ -55,116 +59,187 @@ class AdminStageV1QueryServiceIntegrationTest extends ApplicationIntegrationTest
LocalDate _2077년_6월_16일 = LocalDate.parse("2077-06-16");
LocalDate _2077년_6월_17일 = LocalDate.parse("2077-07-17");

@Test
void 존재하지_않는_축제의_식별자로_조회하면_빈_리스트가_반환된다() {
// when
var actual = adminStageV1QueryService.findAllByFestivalId(4885L);

// then
assertThat(actual).isEmpty();
}

@Nested
class 축제에_공연이_없으면 {

Long 축제_식별자;

@BeforeEach
void setUp() {
var 학교 = schoolRepository.save(SchoolFixture.builder().build());
축제_식별자 = festivalRepository.save(FestivalFixture.builder()
.startDate(_2077년_6월_15일)
.endDate(_2077년_6월_15일)
.school(학교)
.build()).getId();
}
class findAllByFestivalId {

@Test
void 빈_리스트가_반환된다() {
void 존재하지_않는_축제의_식별자로_조회하면_빈_리스트가_반환된다() {
// when
var actual = adminStageV1QueryService.findAllByFestivalId(축제_식별자);
var actual = adminStageV1QueryService.findAllByFestivalId(4885L);

// then
assertThat(actual).isEmpty();
}
}

/**
* 6월 15일 ~ 6월 17일까지 진행되는 축제<p> 6월 15일 공연, 6월 16일 공연이 있다.<p>
* <p>
* 6월 15일 공연에는 아티스트A, 아티스트B가 참여한다.<p> 6월 16일 공연에는 아티스트C가 참여한다.
*/
@Nested
class 특정_축제의_공연_목록과_참여하는_아티스트를_조회할_수_있어야_한다 {

Long 아티스트A_식별자;
Long 아티스트B_식별자;
Long 아티스트C_식별자;
Festival 축제;
Long _6월_15일_공연_식별자;
Long _6월_16일_공연_식별자;

@BeforeEach
void setUp() {
아티스트A_식별자 = createArtist("아티스트A");
아티스트B_식별자 = createArtist("아티스트B");
아티스트C_식별자 = createArtist("아티스트C");
var 학교 = schoolRepository.save(SchoolFixture.builder().build());
축제 = festivalRepository.save(FestivalFixture.builder()
.startDate(_2077년_6월_15일)
.endDate(_2077년_6월_17일)
.school(학교)
.build());
_6월_15일_공연_식별자 = createStage(축제, _2077년_6월_15일, List.of(아티스트A_식별자, 아티스트B_식별자)).getId();
_6월_16일_공연_식별자 = createStage(축제, _2077년_6월_16일, List.of(아티스트C_식별자)).getId();
}
@Nested
class 축제에_공연이_없으면 {

Long 축제_식별자;

@BeforeEach
void setUp() {
var 학교 = schoolRepository.save(SchoolFixture.builder().build());
축제_식별자 = festivalRepository.save(FestivalFixture.builder()
.startDate(_2077년_6월_15일)
.endDate(_2077년_6월_15일)
.school(학교)
.build()).getId();
}

private Long createArtist(String artistName) {
return artistRepository.save(ArtistFixture.builder()
.name(artistName)
.build()
).getId();
@Test
void 빈_리스트가_반환된다() {
// when
var actual = adminStageV1QueryService.findAllByFestivalId(축제_식별자);

// then
assertThat(actual).isEmpty();
}
}

private Stage createStage(Festival festival, LocalDate localDate, List<Long> artistIds) {
var 공연 = stageRepository.save(StageFixture.builder()
.festival(festival)
.startTime(localDate.atTime(18, 0))
.build()
);
for (Long artistId : artistIds) {
stageArtistRepository.save(StageArtistFixture.builder(공연.getId(), artistId).build());
/**
* 6월 15일 ~ 6월 17일까지 진행되는 축제<p> 6월 15일 공연, 6월 16일 공연이 있다.<p>
* <p>
* 6월 15일 공연에는 아티스트A, 아티스트B가 참여한다.<p> 6월 16일 공연에는 아티스트C가 참여한다.
*/
@Nested
class 축제에_공연이_있으면 {

Long 아티스트A_식별자;
Long 아티스트B_식별자;
Long 아티스트C_식별자;
Festival 축제;
Long _6월_15일_공연_식별자;
Long _6월_16일_공연_식별자;

@BeforeEach
void setUp() {
아티스트A_식별자 = createArtist("아티스트A").getId();
아티스트B_식별자 = createArtist("아티스트B").getId();
아티스트C_식별자 = createArtist("아티스트C").getId();
var 학교 = schoolRepository.save(SchoolFixture.builder().build());
축제 = festivalRepository.save(FestivalFixture.builder()
.startDate(_2077년_6월_15일)
.endDate(_2077년_6월_17일)
.school(학교)
.build());
_6월_15일_공연_식별자 = createStage(축제, _2077년_6월_15일, List.of(아티스트A_식별자, 아티스트B_식별자)).getId();
_6월_16일_공연_식별자 = createStage(축제, _2077년_6월_16일, List.of(아티스트C_식별자)).getId();
}

@Test
void 공연의_시작_순서대로_정렬된다() {
// when
var actual = adminStageV1QueryService.findAllByFestivalId(축제.getId());

// then
assertThat(actual)
.map(AdminStageV1Response::id)
.containsExactly(_6월_15일_공연_식별자, _6월_16일_공연_식별자);
}

@Test
void 해당_일자의_공연에_참여하는_아티스트_목록을_조회할_수_있다() {
// when
var stageIdToArtists = adminStageV1QueryService.findAllByFestivalId(축제.getId()).stream()
.collect(toMap(AdminStageV1Response::id, AdminStageV1Response::artists));

// then
assertSoftly(softly -> {
softly.assertThat(stageIdToArtists.get(_6월_15일_공연_식별자))
.map(AdminStageArtistV1Response::id)
.containsExactlyInAnyOrder(아티스트A_식별자, 아티스트B_식별자);

softly.assertThat(stageIdToArtists.get(_6월_16일_공연_식별자))
.map(AdminStageArtistV1Response::id)
.containsExactlyInAnyOrder(아티스트C_식별자);
});
}
return 공연;
}
}

@Test
void 공연의_시작_순서대로_정렬된다() {
// when
var actual = adminStageV1QueryService.findAllByFestivalId(축제.getId());
private Artist createArtist(String artistName) {
return artistRepository.save(ArtistFixture.builder()
.name(artistName)
.build()
);
}

// then
assertThat(actual)
.map(AdminStageV1Response::id)
.containsExactly(_6월_15일_공연_식별자, _6월_16일_공연_식별자);
private Stage createStage(Festival festival, LocalDate localDate, List<Long> artistIds) {
var 공연 = stageRepository.save(StageFixture.builder()
.festival(festival)
.startTime(localDate.atTime(18, 0))
.build()
);
for (Long artistId : artistIds) {
stageArtistRepository.save(StageArtistFixture.builder(공연.getId(), artistId).build());
}
return 공연;
}

@Test
void 해당_일자의_공연에_참여하는_아티스트_목록을_조회할_수_있다() {
// when
var stageIdToArtists = adminStageV1QueryService.findAllByFestivalId(축제.getId()).stream()
.collect(toMap(AdminStageV1Response::id, AdminStageV1Response::artists));
@Nested
class findById {

// then
assertSoftly(softly -> {
softly.assertThat(stageIdToArtists.get(_6월_15일_공연_식별자))
.map(AdminStageArtistV1Response::id)
.containsExactlyInAnyOrder(아티스트A_식별자, 아티스트B_식별자);

softly.assertThat(stageIdToArtists.get(_6월_16일_공연_식별자))
.map(AdminStageArtistV1Response::id)
.containsExactlyInAnyOrder(아티스트C_식별자);
});
@Nested
class 식별자에_해당하는_공연이_없으면 {

@Test
void 예외가_발생한다() {
// when & then
assertThatThrownBy(() -> adminStageV1QueryService.findById(4885L))
.isInstanceOf(NotFoundException.class)
.hasMessage(ErrorCode.STAGE_NOT_FOUND.getMessage());
}
}

@Nested
class 식별자에_해당하는_공연이_있으면 {

Artist 아티스트A;
Artist 아티스트B;
Artist 아티스트C;
Stage 공연;

@BeforeEach
void setUp() {
var 학교 = schoolRepository.save(SchoolFixture.builder().build());
var 축제 = festivalRepository.save(FestivalFixture.builder()
.startDate(_2077년_6월_15일)
.endDate(_2077년_6월_15일)
.school(학교)
.build()
);
아티스트A = createArtist("아티스트A");
아티스트B = createArtist("아티스트B");
아티스트C = createArtist("아티스트C");
공연 = createStage(
축제,
_2077년_6월_15일,
List.of(아티스트A.getId(), 아티스트B.getId(), 아티스트C.getId())
);
}

@Test
void 공연의_정보가_정확하게_조회되어야_한다() {
// when
var actual = adminStageV1QueryService.findById(공연.getId());

assertSoftly(softly -> {
softly.assertThat(actual.id()).isEqualTo(공연.getId());
softly.assertThat(actual.startDateTime()).isEqualTo(공연.getStartTime());
softly.assertThat(actual.ticketOpenTime()).isEqualTo(공연.getTicketOpenTime());
});
}

@Test
void 공연의_아티스트_목록이_조회되어야_한다() {
// when
var actual = adminStageV1QueryService.findById(공연.getId());

// then
assertThat(actual.artists())
.map(AdminStageArtistV1Response::name)
.containsExactlyInAnyOrder(아티스트A.getName(), 아티스트B.getName(), 아티스트C.getName());
}
}
}
}
Loading

0 comments on commit 5071418

Please sign in to comment.