Skip to content

Commit

Permalink
Merge pull request #31 from dnd-side-project/feature/#23
Browse files Browse the repository at this point in the history
특정 프로젝트에 따른 피드백 리스트 조회 api #23
  • Loading branch information
woo0doo authored Feb 17, 2024
2 parents 58ed21e + e319493 commit 1ebb87b
Show file tree
Hide file tree
Showing 13 changed files with 329 additions and 45 deletions.
18 changes: 18 additions & 0 deletions src/docs/asciidoc/api/feedback.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,21 @@ include::{snippets}/feedback/submit/http-response.adoc[]
- Response body
include::{snippets}/feedback/submit/response-fields.adoc[]

[[feedback-list]]
== 특정 프로젝트의 피드백 리스트 조회

=== 성공

==== HTTP Requests
include::{snippets}/feedback/list/http-request.adoc[]
- Path parameters +
include::{snippets}/feedback/list/path-parameters.adoc[]
- Header +
로그인 한 사용자면 보내주시고 아니면 안보내주셔도 됩니다
include::{snippets}/feedback/list/request-headers.adoc[]


==== HTTP Response
include::{snippets}/feedback/list/http-response.adoc[]
- Response body
include::{snippets}/feedback/list/response-fields.adoc[]
3 changes: 3 additions & 0 deletions src/docs/asciidoc/api/project.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ include::{snippets}/project/save/response-fields.adoc[]
include::{snippets}/project/detail/http-request.adoc[]
- Path parameters +
include::{snippets}/project/detail/path-parameters.adoc[]
- Header +
로그인 한 사용자면 보내주시고 아니면 안보내주셔도 됩니다
include::{snippets}/project/detail/request-headers.adoc[]

==== HTTP Response
include::{snippets}/project/detail/http-response.adoc[]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@
import com.sendback.domain.feedback.dto.request.SaveFeedbackRequestDto;
import com.sendback.domain.feedback.dto.response.FeedbackDetailResponseDto;
import com.sendback.domain.feedback.dto.response.FeedbackIdResponseDto;
import com.sendback.domain.feedback.dto.response.GetFeedbacksResponse;
import com.sendback.domain.feedback.dto.response.SubmitFeedbackResponseDto;
import com.sendback.domain.feedback.service.FeedbackService;
import com.sendback.global.common.ApiResponse;
import com.sendback.global.common.UserId;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.MediaType;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

Expand Down Expand Up @@ -45,4 +48,17 @@ public ApiResponse<SubmitFeedbackResponseDto> submitFeedback(
@ModelAttribute MultipartFile file) {
return success(feedbackService.submitFeedback(userId, feedbackId, file));
}

@GetMapping("/{projectId}/feedbacks")
public ApiResponse<GetFeedbacksResponse> getfeedbacks(
@PathVariable Long projectId) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

if (authentication.getPrincipal() == "anonymousUser") {
return success(feedbackService.getFeedbacks(null , projectId));
}

Long userId = (Long) authentication.getPrincipal();
return success(feedbackService.getFeedbacks(userId, projectId));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.sendback.domain.feedback.dto.response;

import com.sendback.domain.feedback.entity.Feedback;

public record FeedbackResponseDto(
Long feedbackId,
String title,
String rewardMessage,
String startedAt,
String endedAt,
boolean isFinished,
boolean isAuthor,
boolean isSubmitted
) {
public static FeedbackResponseDto of(Feedback feedback, boolean isAuthor, boolean isSubmitted) {
return new FeedbackResponseDto(
feedback.getId(),
feedback.getTitle(),
feedback.getRewardMessage(),
feedback.getStartedAt().toString(),
feedback.getEndedAt().toString(),
feedback.isFinished(),
isAuthor,
isSubmitted
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.sendback.domain.feedback.dto.response;

import java.util.List;

public record GetFeedbacksResponse(
List<FeedbackResponseDto> feedbacks
) {}
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
package com.sendback.domain.feedback.repository;

import com.sendback.domain.feedback.entity.Feedback;
import com.sendback.domain.project.entity.Project;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.List;

public interface FeedbackRepository extends JpaRepository<Feedback, Long> {

List<Feedback> findTop3ByProjectAndIsDeletedIsFalseOrderByIdDesc(Project project);
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
package com.sendback.domain.feedback.service;

import com.sendback.domain.feedback.dto.request.SaveFeedbackRequestDto;
import com.sendback.domain.feedback.dto.response.FeedbackDetailResponseDto;
import com.sendback.domain.feedback.dto.response.FeedbackIdResponseDto;
import com.sendback.domain.feedback.dto.response.SubmitFeedbackResponseDto;
import com.sendback.domain.feedback.dto.response.*;
import com.sendback.domain.feedback.entity.Feedback;
import com.sendback.domain.feedback.entity.FeedbackSubmit;
import com.sendback.domain.feedback.repository.FeedbackRepository;
Expand All @@ -21,6 +19,8 @@
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.util.List;

import static com.sendback.domain.feedback.exception.FeedbackExceptionType.DUPLICATE_FEEDBACK_SUBMIT;
import static com.sendback.domain.feedback.exception.FeedbackExceptionType.NOT_FOUND_FEEDBACK;

Expand Down Expand Up @@ -55,6 +55,30 @@ public FeedbackDetailResponseDto getFeedbackDetail(Long projectId, Long feedback
return FeedbackDetailResponseDto.from(feedback);
}

public GetFeedbacksResponse getFeedbacks(Long userId, Long projectId) {
Project project = projectService.getProjectById(projectId);
List<Feedback> feedbacks = feedbackRepository.findTop3ByProjectAndIsDeletedIsFalseOrderByIdDesc(project);

if (userId == null) {
List<FeedbackResponseDto> feedbackResponsDtos = feedbacks.stream()
.map(feedback -> FeedbackResponseDto.of(feedback, false, false)).toList();
return new GetFeedbacksResponse(feedbackResponsDtos);
}

User loginUser = userService.getUserById(userId);
boolean isAuthor = project.isAuthor(loginUser);

List<FeedbackResponseDto> feedbackResponsDtos = feedbacks.stream()
.map(feedback -> FeedbackResponseDto.of(feedback, isAuthor, checkSubmit(loginUser, feedback)))
.toList();

return new GetFeedbacksResponse(feedbackResponsDtos);
}

private boolean checkSubmit(User loginUser, Feedback feedback) {
return feedbackSubmitRepository.existsByUserAndFeedbackAndIsDeletedIsFalse(loginUser, feedback);
}

public Feedback getFeedback(Long feedbackId) {
return feedbackRepository.findById(feedbackId)
.orElseThrow(() -> new NotFoundException(NOT_FOUND_FEEDBACK));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ public class SecurityConfig {

private final String[] GET_METHOD_PERMITTED_URLS = {
"/api/projects/{projectId}/feedbacks/{feedbackId}",
"/api/projects/{projectId}"
"/api/projects/{projectId}",
"/api/projects/{projectId}/feedbacks"
};

@Bean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.sendback.domain.feedback.dto.request.SaveFeedbackRequestDto;
import com.sendback.domain.feedback.dto.response.FeedbackDetailResponseDto;
import com.sendback.domain.feedback.dto.response.FeedbackIdResponseDto;
import com.sendback.domain.feedback.dto.response.GetFeedbacksResponse;
import com.sendback.domain.feedback.dto.response.SubmitFeedbackResponseDto;
import com.sendback.global.ControllerTest;
import com.sendback.global.WithMockCustomUser;
Expand Down Expand Up @@ -226,4 +227,74 @@ public void success() throws Exception {

}
}

@Nested
@DisplayName("프로젝트에 따라 최신 순으로 피드백 3개 이하를 조회할 때")
class getFeedbacks {

GetFeedbacksResponse getFeedbacksResponse = MOCK_GET_FEEDBACK_RESPONSE;

@Test
@DisplayName("정상적인 접근 시 값을 반환한다.")
@WithMockCustomUser
public void success() throws Exception {
//given
Long projectId = 1L;
given(feedbackService.getFeedbacks(any(), anyLong())).willReturn(getFeedbacksResponse);

//when
ResultActions resultActions = mockMvc.perform(get("/api/projects/{projectId}/feedbacks", projectId)
.header(HttpHeaders.AUTHORIZATION, ACCESS_TOKEN_PREFIX + "AccessToken")
.with(csrf().asHeader()))
.andDo(print());

//then
resultActions
.andDo(document("feedback/list",
customRequestPreprocessor(),
preprocessResponse(prettyPrint()),
pathParameters(
parameterWithName("projectId").description("프로젝트 ID")
),
requestHeaders(
headerWithName("Authorization").description("JWT 엑세스 토큰").optional()
),
responseFields(
fieldWithPath("code").type(JsonFieldType.NUMBER)
.description("코드"),
fieldWithPath("data").type(JsonFieldType.OBJECT).description("데이터"),
fieldWithPath("data.feedbacks").type(JsonFieldType.ARRAY).description("피드백 리스트"),
fieldWithPath("data.feedbacks[].feedbackId").type(JsonFieldType.NUMBER).description("피드백 ID"),
fieldWithPath("data.feedbacks[].title").type(JsonFieldType.STRING).description("제목"),
fieldWithPath("data.feedbacks[].rewardMessage").type(JsonFieldType.STRING).description("추가 리워드"),
fieldWithPath("data.feedbacks[].startedAt").type(JsonFieldType.STRING).description("시작 날짜"),
fieldWithPath("data.feedbacks[].endedAt").type(JsonFieldType.STRING).description("끝나는 날짜"),
fieldWithPath("data.feedbacks[].isFinished").type(JsonFieldType.BOOLEAN).description("피드백 종료 여부"),
fieldWithPath("data.feedbacks[].isAuthor").type(JsonFieldType.BOOLEAN).description("작성자 여부"),
fieldWithPath("data.feedbacks[].isSubmitted").type(JsonFieldType.BOOLEAN).description("제출 여부"),
fieldWithPath("message").type(JsonFieldType.STRING)
.description("메시지")
)))
.andExpect(jsonPath("$.code").value("200"))
.andExpect(jsonPath("$.message").value("성공"))
.andExpect(jsonPath("$.data.feedbacks").isArray())
.andExpect(jsonPath("$.data.feedbacks[0].feedbackId").value(MOCK_FEEDBACK_RESPONSE_DTO_A.feedbackId()))
.andExpect(jsonPath("$.data.feedbacks[0].title").value(MOCK_FEEDBACK_RESPONSE_DTO_A.title()))
.andExpect(jsonPath("$.data.feedbacks[0].rewardMessage").value(MOCK_FEEDBACK_RESPONSE_DTO_A.rewardMessage()))
.andExpect(jsonPath("$.data.feedbacks[0].startedAt").value(MOCK_FEEDBACK_RESPONSE_DTO_A.startedAt()))
.andExpect(jsonPath("$.data.feedbacks[0].endedAt").value(MOCK_FEEDBACK_RESPONSE_DTO_A.endedAt()))
.andExpect(jsonPath("$.data.feedbacks[0].isFinished").value(MOCK_FEEDBACK_RESPONSE_DTO_A.isFinished()))
.andExpect(jsonPath("$.data.feedbacks[0].isAuthor").value(MOCK_FEEDBACK_RESPONSE_DTO_A.isAuthor()))
.andExpect(jsonPath("$.data.feedbacks[0].isSubmitted").value(MOCK_FEEDBACK_RESPONSE_DTO_A.isSubmitted()))
.andExpect(jsonPath("$.data.feedbacks[1].feedbackId").value(MOCK_FEEDBACK_RESPONSE_DTO_B.feedbackId()))
.andExpect(jsonPath("$.data.feedbacks[1].title").value(MOCK_FEEDBACK_RESPONSE_DTO_B.title()))
.andExpect(jsonPath("$.data.feedbacks[1].rewardMessage").value(MOCK_FEEDBACK_RESPONSE_DTO_B.rewardMessage()))
.andExpect(jsonPath("$.data.feedbacks[1].startedAt").value(MOCK_FEEDBACK_RESPONSE_DTO_B.startedAt()))
.andExpect(jsonPath("$.data.feedbacks[1].endedAt").value(MOCK_FEEDBACK_RESPONSE_DTO_B.endedAt()))
.andExpect(jsonPath("$.data.feedbacks[1].isFinished").value(MOCK_FEEDBACK_RESPONSE_DTO_B.isFinished()))
.andExpect(jsonPath("$.data.feedbacks[1].isAuthor").value(MOCK_FEEDBACK_RESPONSE_DTO_B.isAuthor()))
.andExpect(jsonPath("$.data.feedbacks[1].isSubmitted").value(MOCK_FEEDBACK_RESPONSE_DTO_B.isSubmitted()))
.andExpect(status().isOk());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import com.sendback.domain.feedback.dto.request.SaveFeedbackRequestDto;
import com.sendback.domain.feedback.dto.response.FeedbackDetailResponseDto;
import com.sendback.domain.feedback.dto.response.FeedbackResponseDto;
import com.sendback.domain.feedback.dto.response.GetFeedbacksResponse;
import com.sendback.domain.feedback.dto.response.SubmitFeedbackResponseDto;
import com.sendback.domain.feedback.entity.Feedback;
import com.sendback.domain.feedback.entity.FeedbackSubmit;
Expand All @@ -10,6 +12,7 @@
import com.sendback.global.common.constants.Level;

import java.time.LocalDate;
import java.util.List;

import static com.sendback.domain.project.entity.Progress.PLANNING;
import static com.sendback.global.common.constants.FieldName.ART;
Expand All @@ -35,6 +38,22 @@ public class FeedbackFixture {
public static final SubmitFeedbackResponseDto MOCK_SUBMIT_FEEDBACK_RESPONSE = new SubmitFeedbackResponseDto(
Level.ONE.getName(), false, 4L);

public static final FeedbackResponseDto MOCK_FEEDBACK_RESPONSE_DTO_A = new FeedbackResponseDto(
1L, "기획 피드백 부탁해요", "아메리카노 5개",
LocalDate.of(2024, 1, 12).toString(),
LocalDate.of(2024, 1, 15).toString(), false, false, false
);

public static final FeedbackResponseDto MOCK_FEEDBACK_RESPONSE_DTO_B = new FeedbackResponseDto(
2L, "와이어 프레임 피드백 부탁해요", "빙수 5개",
LocalDate.of(2024, 1, 15).toString(),
LocalDate.of(2024, 1, 18).toString(), true, true, false
);

public static final GetFeedbacksResponse MOCK_GET_FEEDBACK_RESPONSE = new GetFeedbacksResponse(
List.of(MOCK_FEEDBACK_RESPONSE_DTO_A, MOCK_FEEDBACK_RESPONSE_DTO_B)
);

public static FeedbackSubmit createDummyFeedbackSubmit(User user, Feedback feedback) {
return FeedbackSubmit.of(user, feedback, SCREEN_SHOT_URL);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package com.sendback.domain.feedback.repository;

import com.sendback.domain.feedback.entity.Feedback;
import com.sendback.domain.feedback.persister.FeedbackTestPersister;
import com.sendback.global.RepositoryTest;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;

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

public class FeedbackRepositoryTest extends RepositoryTest {

@Autowired
FeedbackRepository feedbackRepository;

@Nested
@DisplayName("feedback 테이블에서 삭제되지 않은 가장 최신의 피드백 3개 이하를 조회하는 경우")
class findTop3ByProjectAndIsDeletedIsFalseOrderByIdDesc {

@Test
@DisplayName("한 개인 경우 한 개만 반환한다.")
public void success_oneFeedback() throws Exception {
//given
Feedback feedback = feedbackTestPersister.builder().save();

//when
List<Feedback> response = feedbackRepository.findTop3ByProjectAndIsDeletedIsFalseOrderByIdDesc(feedback.getProject());

//then
assertThat(response.size()).isEqualTo(1);

}

@Test
@DisplayName("두 개인 경우 두 개만 반환한다.")
public void success_twoFeedback() throws Exception {
//given
FeedbackTestPersister.FeedbackBuilder feedbackBuilder = feedbackTestPersister.builder();
Feedback feedback_A = feedbackBuilder.save();
feedbackBuilder.project(feedback_A.getProject()).save();

//when
List<Feedback> response = feedbackRepository.findTop3ByProjectAndIsDeletedIsFalseOrderByIdDesc(feedback_A.getProject());

//then
assertThat(response.size()).isEqualTo(2);

}

@Test
@DisplayName("세 개 이상인 경우 최신 세 개만 반환한다.")
public void success_threeFeedback() throws Exception {
//given
FeedbackTestPersister.FeedbackBuilder feedbackBuilder = feedbackTestPersister.builder();
Feedback feedback_A = feedbackBuilder.save();
Feedback feedback_third = feedbackBuilder.project(feedback_A.getProject()).save();
Feedback feedback_second = feedbackBuilder.project(feedback_A.getProject()).save();
Feedback feedback_first = feedbackBuilder.project(feedback_A.getProject()).save();

//when
List<Feedback> response = feedbackRepository.findTop3ByProjectAndIsDeletedIsFalseOrderByIdDesc(feedback_A.getProject());

//then
assertThat(response.size()).isEqualTo(3);
assertThat(response.get(0)).usingRecursiveComparison().isEqualTo(feedback_first);
assertThat(response.get(1)).usingRecursiveComparison().isEqualTo(feedback_second);
assertThat(response.get(2)).usingRecursiveComparison().isEqualTo(feedback_third);
}
}
}
Loading

0 comments on commit 1ebb87b

Please sign in to comment.