Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat-be: 대시보드 생성 #65

Merged
merged 4 commits into from
Jul 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/main/java/com/cruru/club/domain/Club.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ public class Club {
@JoinColumn(name = "member_id")
private Member member;

public Club(String name, Member member) {
this.name = name;
this.member = member;
}

@Override
public boolean equals(Object o) {
if (this == o) {
Expand Down
12 changes: 12 additions & 0 deletions src/main/java/com/cruru/club/exception/ClubNotFoundException.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.cruru.club.exception;

import com.cruru.advice.NotFoundException;

public class ClubNotFoundException extends NotFoundException {

private static final String MESSAGE = "존재하지 않는 동아리입니다.";

public ClubNotFoundException() {
super(MESSAGE);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.cruru.dashboard.controller;

import com.cruru.dashboard.controller.dto.DashboardCreateDto;
import com.cruru.dashboard.service.DashboardService;
import com.cruru.member.controller.dto.MemberCreateResponse;
import java.net.URI;
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.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/v1/dashboards")
@RequiredArgsConstructor
public class DashboardController {

private final DashboardService dashboardService;

@PostMapping
public ResponseEntity<MemberCreateResponse> create(
@RequestParam(name = "club_id") Long clubId,
@RequestBody DashboardCreateDto request) {
Long dashboardId = dashboardService.create(clubId, request);
Chocochip101 marked this conversation as resolved.
Show resolved Hide resolved
return ResponseEntity.created(URI.create("/v1/dashboards/" + dashboardId)).build();
Chocochip101 marked this conversation as resolved.
Show resolved Hide resolved
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.cruru.dashboard.controller.dto;

public record DashboardCreateDto(String name) {

Chocochip101 marked this conversation as resolved.
Show resolved Hide resolved
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@

public class DashboardNotFoundException extends NotFoundException {

private static final String message = "존재하지 않는 대시보드입니다.";
private static final String MESSAGE = "존재하지 않는 대시보드입니다.";

public DashboardNotFoundException() {
super(message);
super(MESSAGE);
}
}
52 changes: 52 additions & 0 deletions src/main/java/com/cruru/dashboard/service/DashboardService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.cruru.dashboard.service;

import com.cruru.club.domain.Club;
import com.cruru.club.domain.repository.ClubRepository;
import com.cruru.club.exception.ClubNotFoundException;
import com.cruru.dashboard.controller.dto.DashboardCreateDto;
import com.cruru.dashboard.domain.Dashboard;
import com.cruru.dashboard.domain.repository.DashboardRepository;
import com.cruru.process.domain.Process;
import com.cruru.process.domain.repository.ProcessRepository;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
public class DashboardService {

private static final Process DEFAULT_FIRST_PROCESS = new Process(1, "지원 접수", "지원자가 이력서를 제출하는 단계", null);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

논의를 통해 Fixture을 만드는 것을 고려해봅시다!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixture는 Test Fixture에서 사용되는 용어인데, 의도한 바가 맞을까요?

https://docs.spring.io/spring-framework/reference/testing/testcontext-framework/fixture-di.html

private static final Process DEFAULT_LAST_PROCESS = new Process(2, "합격", "지원자가 최종적으로 합격한 단계", null);
Comment on lines +21 to +22
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

서류지원, 합격 프로세스는 고정값이므로 static으로 두었습니다. 대시보드 생성시, 해당 프로세스의 내용을 뽑아와 저장하도록 했습니다. 이 방식보다 나은 방법이 있으시면 제안부탁드려요.

Comment on lines +21 to +22
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

static으로 정의하신 이유가 있나요?

Copy link
Contributor

@xogns1514 xogns1514 Jul 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

프로세스 2개가 고정값이여서, static으로 두었습니다.
name, decription 각각에 대해 상수화 하는 것보다, Process 객체 자체를 static으로 두는 것이 관리하기 편할 것 같다고 생각했습니다. 초코칩의 생각은 어떠신가용?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

name, decription 각각에 대해 상수화와는 별개로, Process 객체로 관리해도 괜찮을 것 같아요.

러쉬는 어떤 측면에서 관리의 용이성을 느꼈나요?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아직 인스턴스로 관리했을 경우와 static으로 관리했을 경우 성능적 차이는 모르겠네요.. 고민해보겠습니다.


private final DashboardRepository dashboardRepository;
private final ClubRepository clubRepository;
private final ProcessRepository processRepository;

@Transactional
public Long create(long clubId, DashboardCreateDto request) {
Chocochip101 marked this conversation as resolved.
Show resolved Hide resolved
Club club = clubRepository.findById(clubId).orElseThrow(ClubNotFoundException::new);

Dashboard savedDashboard = dashboardRepository.save(new Dashboard(request.name(), club));

Process firstProcess = new Process(
DEFAULT_FIRST_PROCESS.getSequence(),
DEFAULT_FIRST_PROCESS.getName(),
DEFAULT_FIRST_PROCESS.getDescription(),
savedDashboard
);

Process lastProcess = new Process(
DEFAULT_LAST_PROCESS.getSequence(),
DEFAULT_LAST_PROCESS.getName(),
DEFAULT_LAST_PROCESS.getDescription(),
savedDashboard
);

processRepository.saveAll(List.of(firstProcess, lastProcess));

return savedDashboard.getId();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.cruru.dashboard.controller;

import com.cruru.club.domain.Club;
import com.cruru.club.domain.repository.ClubRepository;
import com.cruru.dashboard.controller.dto.DashboardCreateDto;
import io.restassured.RestAssured;
import io.restassured.http.ContentType;
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.web.server.LocalServerPort;

@DisplayName("대시보드 컨트롤러 테스트")
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class DashboardControllerTest {

@LocalServerPort
private int port;

@Autowired
private ClubRepository clubRepository;

private Club club;

@BeforeEach
void setUp() {
RestAssured.port = port;
club = clubRepository.save(new Club("크루루 동아리", null));
}

@DisplayName("대시보드 생성 성공 시, 201을 응답한다.")
@Test
void read() {
DashboardCreateDto request = new DashboardCreateDto("크루루대시보드");

RestAssured.given().log().all()
.contentType(ContentType.JSON)
.body(request)
.when().post("/v1/dashboards?club_id=" + club.getId())
.then().log().all().statusCode(201);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package com.cruru.dashboard.service;

import static org.assertj.core.api.Assertions.assertThat;

import com.cruru.applicant.domain.repository.ApplicantRepository;
import com.cruru.club.domain.Club;
import com.cruru.club.domain.repository.ClubRepository;
import com.cruru.dashboard.controller.dto.DashboardCreateDto;
import com.cruru.dashboard.domain.repository.DashboardRepository;
import com.cruru.process.domain.Process;
import com.cruru.process.domain.repository.ProcessRepository;
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;

@DisplayName("대시보드 서비스 테스트")
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
class DashboardServiceTest {

@Autowired
DashboardService dashboardService;

@Autowired
DashboardRepository dashboardRepository;

@Autowired
ProcessRepository processRepository;

@Autowired
ClubRepository clubRepository;

@Autowired
ApplicantRepository applicantRepository;

@BeforeEach
void setUp() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 부분도 abstract class의 상위 test class를 만들어서 상속받아 전역적으로 test class를 만들면 좋을 것 같습니다

applicantRepository.deleteAll();
processRepository.deleteAll();
dashboardRepository.deleteAll();
clubRepository.deleteAll();
}
Comment on lines +38 to +44
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

기존 processServiceTest에서 남아있던 데이터를 deleteAll()로 처리하는 것을 보고, 컨벤션 맞춰 작성했습니다.
테스트마다 데이터 격리 어떤방식으로 통일할지 얘기해보면 좋을 것 같습니다.


@DisplayName("새로운 대시보드를 생성시, 기본 프로세스 2개가 생성된다.")
@Test
void createDefaultProcessWhenCreateDashBoard() {
// given
Club club = new Club("크루루동아리", null);
clubRepository.save(club);

// when
DashboardCreateDto request = new DashboardCreateDto("크루루대시보드");
Long dashboardId = dashboardService.create(club.getId(), request);

// then
List<Process> processes = processRepository.findAllByDashboardId(dashboardId);
assertThat(processes).hasSize(2);
}
}
Loading