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

(회원) 선호 카테고리 삭제 기능 구현 #79

Merged
merged 13 commits into from
Jul 20, 2023
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
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@
import com.votogether.domain.member.entity.Member;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
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.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
Expand All @@ -28,16 +30,38 @@ public class CategoryController {
@ApiResponse(responseCode = "200", description = "조회 성공")
@GetMapping("/guest")
public ResponseEntity<List<CategoryResponse>> getAllCategories() {
List<CategoryResponse> categories = categoryService.getAllCategories();
final List<CategoryResponse> categories = categoryService.getAllCategories();
return ResponseEntity.status(HttpStatus.OK).body(categories);
}

@Operation(summary = "선호 카테고리 추가하기", description = "선호하는 카테고리를 선호 카테고리 목록에 추가한다.")
@ApiResponse(responseCode = "201", description = "추가 성공")
@ApiResponses({
@ApiResponse(responseCode = "201", description = "추가 성공"),
@ApiResponse(responseCode = "400", description = "해당 카테고리가 추가가 되어 있어 중복 추가 실패"),
@ApiResponse(responseCode = "404", description = "해당 카테고리가 존재하지 않아 추가 실패"),
})
@PostMapping("/{categoryId}/like")
public ResponseEntity<Void> addFavoriteCategory(final Member member, @PathVariable final Long categoryId) {
categoryService.addFavoriteCategory(member, categoryId);
return ResponseEntity.status(HttpStatus.CREATED).build();
}

@Operation(summary = "선호 카테고리 삭제하기", description = "선호하는 카테고리를 선호 카테고리 목록에서 삭제한다.")
@ApiResponses({
@ApiResponse(responseCode = "204", description = "삭제 성공"),
@ApiResponse(responseCode = "400", description = "선호하는 카테고리가 아니여서 삭제 실패"),
@ApiResponse(responseCode = "404", description = "해당 카테고리가 존재하지 않아 삭제 실패")
})
@DeleteMapping("/{categoryId}/like")
public ResponseEntity<Void> removeFavoriteCategory(final Member member, @PathVariable final Long categoryId) {
categoryService.removeFavoriteCategory(member, categoryId);
return ResponseEntity.status(HttpStatus.NO_CONTENT).build();
}

@GetMapping
public ResponseEntity<Void> getAllCategories(final Member member) {
categoryService.getAllCategories(member);
return ResponseEntity.status(HttpStatus.OK).build();
}

}
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
package com.votogether.domain.category.dto.response;

import com.votogether.domain.category.entity.Category;
import java.util.List;

public record CategoryResponse(
Long id,
String name,
boolean isFavorite
) {

public CategoryResponse(final Category category) {
this(category.getId(), category.getName(), false);
public CategoryResponse(final Category category, final boolean isFavorite) {
this(category.getId(), category.getName(), isFavorite);
}

public CategoryResponse(final Category category, final List<Category> favoriteCategories) {
this(category.getId(), category.getName(), favoriteCategories.contains(category));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import com.votogether.domain.member.entity.Member;
import com.votogether.domain.member.entity.MemberCategory;
import com.votogether.domain.member.repository.MemberCategoryRepository;
import java.util.Comparator;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
Expand All @@ -20,27 +21,53 @@ public class CategoryService {

@Transactional(readOnly = true)
public List<CategoryResponse> getAllCategories() {
List<Category> categories = categoryRepository.findAll();
final List<Category> categories = categoryRepository.findAll();

return categories.stream()
.map(CategoryResponse::new)
.sorted(Comparator.comparing(Category::getName))
.map(category -> new CategoryResponse(category, false))
.toList();
}

@Transactional
public void addFavoriteCategory(final Member member, final Long categoryId) {
Category category = categoryRepository.findById(categoryId)
final Category category = categoryRepository.findById(categoryId)
.orElseThrow(() -> new IllegalArgumentException("해당 카테고리가 존재하지 않습니다."));

memberCategoryRepository.findByMemberAndCategory(member, category)
.ifPresent(ignore -> new IllegalStateException("이미 선호 카테고리에 등록되어 있습니다."));

MemberCategory memberCategory = MemberCategory.builder()
final MemberCategory memberCategory = MemberCategory.builder()
.member(member)
.category(category)
.build();

memberCategoryRepository.save(memberCategory);
}

@Transactional
public void removeFavoriteCategory(final Member member, final Long categoryId) {
final Category category = categoryRepository.findById(categoryId)
.orElseThrow(() -> new IllegalArgumentException("해당 카테고리가 존재하지 않습니다."));
final MemberCategory memberCategory = memberCategoryRepository.findByMemberAndCategory(member, category)
.orElseThrow(() -> new IllegalArgumentException("해당 카테고리는 선호 카테고리가 아닙니다."));

memberCategoryRepository.delete(memberCategory);
}

@Transactional(readOnly = true)
public List<CategoryResponse> getAllCategories(final Member member) {
final List<Category> categories = categoryRepository.findAll();
final List<MemberCategory> memberCategories = memberCategoryRepository.findByMember(member);

final List<Category> favoriteCategories = memberCategories.stream()
.map(MemberCategory::getCategory)
.toList();

return categories.stream()
.sorted(Comparator.comparing(Category::getName))
.map(category -> new CategoryResponse(category, favoriteCategories))
.toList();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,14 @@
import com.votogether.domain.category.entity.Category;
import com.votogether.domain.member.entity.Member;
import com.votogether.domain.member.entity.MemberCategory;
import java.util.List;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;

public interface MemberCategoryRepository extends JpaRepository<MemberCategory, Long> {

Optional<MemberCategory> findByMemberAndCategory(final Member member, final Category category);

List<MemberCategory> findByMember(final Member member);

}
Original file line number Diff line number Diff line change
Expand Up @@ -27,20 +27,21 @@ public void vote(
final Long postId,
final Long postOptionId
) {
Post post = postRepository.findById(postId)
final Post post = postRepository.findById(postId)
.orElseThrow(() -> new IllegalArgumentException("해당 게시글이 존재하지 않습니다."));

validateAlreadyVoted(member, post);

PostOption postOption = postOptionRepository.findById(postOptionId)
final PostOption postOption = postOptionRepository.findById(postOptionId)
.orElseThrow(() -> new IllegalArgumentException("해당 선택지가 존재하지 않습니다."));

Vote vote = post.makeVote(member, postOption);
final Vote vote = post.makeVote(member, postOption);
member.plusPoint(1);
voteRepository.save(vote);
}

private void validateAlreadyVoted(Member member, Post post) {

private void validateAlreadyVoted(final Member member, final Post post) {
final PostOptions postOptions = post.getPostOptions();
final List<Vote> alreadyVoted = voteRepository.findByMemberAndPostOptionIn(member, postOptions.getPostOptions());
if (!alreadyVoted.isEmpty()) {
Expand All @@ -54,20 +55,20 @@ public void changeVote(
final Long originPostOptionId,
final Long newPostOptionId
) {
Post post = postRepository.findById(postId)
final Post post = postRepository.findById(postId)
.orElseThrow(() -> new IllegalArgumentException("해당 게시글이 존재하지 않습니다."));

PostOption originPostOption = postOptionRepository.findById(originPostOptionId)
final PostOption originPostOption = postOptionRepository.findById(originPostOptionId)
.orElseThrow(() -> new IllegalArgumentException("헤당 선택지가 존재하지 않습니다."));

Vote originVote = voteRepository.findByMemberAndPostOption(member, originPostOption)
final Vote originVote = voteRepository.findByMemberAndPostOption(member, originPostOption)
.orElseThrow(() -> new IllegalArgumentException("선택지에 해당되는 투표가 존재하지 않습니다."));

PostOption newPostOption = postOptionRepository.findById(newPostOptionId)
final PostOption newPostOption = postOptionRepository.findById(newPostOptionId)
.orElseThrow(() -> new IllegalArgumentException("헤당 선택지가 존재하지 않습니다."));

voteRepository.delete(originVote);
Vote vote = post.makeVote(member, newPostOption);
final Vote vote = post.makeVote(member, newPostOption);
voteRepository.save(vote);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package com.votogether.domain.category.contorller;

import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.nullValue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.doNothing;

import com.votogether.domain.category.dto.response.CategoryResponse;
import com.votogether.domain.category.entity.Category;
import com.votogether.domain.category.service.CategoryService;
import io.restassured.module.mockmvc.RestAssuredMockMvc;
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.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.HttpStatus;

@WebMvcTest(CategoryController.class)
class CategoryControllerTest {

@MockBean
CategoryService categoryService;

@BeforeEach
void setUp() {
RestAssuredMockMvc.standaloneSetup(new CategoryController(categoryService));
}

@Test
@DisplayName("전체 카테고리 목록을 조회한다.")
void getAllCategories() {
// given
Category category = Category.builder()
.name("개발")
.build();
given(categoryService.getAllCategories()).willReturn(List.of(new CategoryResponse(category, false)));

// when
RestAssuredMockMvc.
given().log().all()
.when().get("/categories/guest")
.then().log().all()
.status(HttpStatus.OK)
.body("[0].id", nullValue())
.body("[0].name", equalTo("개발"))
.body("[0].isFavorite", equalTo(false));
}

@Test
@DisplayName("선호하는 카테고리를 선호 카테고리 목록에 추가한다.")
void addFavoriteCategory() {
// given
doNothing().when(categoryService).addFavoriteCategory(any(), any());
Copy link
Collaborator

Choose a reason for hiding this comment

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

Q
doNothing().when()은 어떤 역할을 하나요?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

목 객체 서비스가 해당 메서드를 실행하면 아무것도 응답하지 않는 역할을 합니다


// when & then
RestAssuredMockMvc.
given().log().all()
.when().post("/categories/{categoryId}/like", 1)
.then().log().all()
.status(HttpStatus.CREATED);
}

@Test
@DisplayName("선호 카테고리를 삭제한다.")
void removeFavoriteCategory() {
// given
doNothing().when(categoryService).removeFavoriteCategory(any(), any());

// when & then
RestAssuredMockMvc.
given().log().all()
.when().delete("/categories/{categoryId}/like", 1)
.then().log().all()
.status(HttpStatus.NO_CONTENT);
}

}
Loading