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

[BE] issue47: 태그 목록 검색 및 조회 #57

Merged
merged 4 commits into from
Jul 14, 2022
Merged
Show file tree
Hide file tree
Changes from 3 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
@@ -1,7 +1,6 @@
package com.woowacourse.moamoa.common.config;

import java.util.List;

import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpHeaders;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
Expand All @@ -21,7 +20,7 @@ public void addArgumentResolvers(final List<HandlerMethodArgumentResolver> resol
@Override
public void addCorsMappings(final CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedMethods(ALLOW_METHODS)
.exposedHeaders(HttpHeaders.LOCATION);
.allowedMethods(ALLOW_METHODS)
.exposedHeaders(HttpHeaders.LOCATION);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.woowacourse.moamoa.tag.controller;

import com.woowacourse.moamoa.tag.service.TagService;
import com.woowacourse.moamoa.tag.service.response.TagsResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequiredArgsConstructor
public class TagController {

private final TagService tagService;

@GetMapping("/api/tags")
public ResponseEntity<TagsResponse> getTags(
@RequestParam(value = "tag-name", required = false, defaultValue = "") final String tagName
) {
final TagsResponse tagsResponse = tagService.getTags(tagName);

return ResponseEntity.ok().body(tagsResponse);
}
}
26 changes: 26 additions & 0 deletions backend/src/main/java/com/woowacourse/moamoa/tag/domain/Tag.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.woowacourse.moamoa.tag.domain;

import static javax.persistence.GenerationType.IDENTITY;
import static lombok.AccessLevel.PROTECTED;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;

@AllArgsConstructor
@Getter
@Entity
@NoArgsConstructor(access = PROTECTED)
public class Tag {

@Id
@GeneratedValue(strategy = IDENTITY)
private Long id;

@Column(name = "tag_name")
private String name;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.woowacourse.moamoa.tag.domain.repository;

import com.woowacourse.moamoa.tag.domain.Tag;
import org.springframework.data.jpa.repository.JpaRepository;

public interface JpaTagRepository extends JpaRepository<Tag, Long>, TagRepository {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.woowacourse.moamoa.tag.domain.repository;

import com.woowacourse.moamoa.tag.domain.Tag;
import java.util.List;

public interface TagRepository {

List<Tag> findAllByNameContainingIgnoreCase(String name);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.woowacourse.moamoa.tag.service;

import com.woowacourse.moamoa.tag.domain.Tag;
import com.woowacourse.moamoa.tag.domain.repository.TagRepository;
import com.woowacourse.moamoa.tag.service.response.TagResponse;
import com.woowacourse.moamoa.tag.service.response.TagsResponse;
import java.util.List;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

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

private final TagRepository tagRepository;

public TagsResponse getTags(final String tagName) {
final List<Tag> tags = tagRepository.findAllByNameContainingIgnoreCase(tagName.trim());
final List<TagResponse> tagsResponse = tags.stream()
.map(TagResponse::new)
.collect(Collectors.toList());
return new TagsResponse(tagsResponse);
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

EOF! 😱

Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.woowacourse.moamoa.tag.service.response;

import com.woowacourse.moamoa.tag.domain.Tag;
import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
public class TagResponse {

private Long id;
private String tagName;

public TagResponse(Tag tag) {
this(tag.getId(), tag.getName());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.woowacourse.moamoa.tag.service.response;

import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Getter;

@AllArgsConstructor
@Getter
public class TagsResponse {

private List<TagResponse> tags;
}
1 change: 0 additions & 1 deletion backend/src/main/resources/application.properties

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@ void setUp() {
@Test
void corsTest() {
RestAssured.given()
.header("Origin", "https://xxx.com")
.header("Access-Control-Request-Method", "GET")
.when()
.options("/api/studies")
.then()
.statusCode(200);
.header("Origin", "https://xxx.com")
.header("Access-Control-Request-Method", "GET")
.when()
.options("/api/studies")
.then()
.statusCode(200);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package com.woowacourse.acceptance.tag;

import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.not;

import com.woowacourse.acceptance.AcceptanceTest;
import io.restassured.RestAssured;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.http.HttpStatus;

public class TagsAcceptanceTest extends AcceptanceTest {

@DisplayName("전체 태그 목록을 조회한다.")
@Test
void getAllTags() {
RestAssured.given().log().all()
.when().log().all()
.get("/api/tags")
.then().log().all()
.statusCode(HttpStatus.OK.value())
.body("tags", hasSize(5))
.body("tags.id", not(empty()))
Copy link
Collaborator

Choose a reason for hiding this comment

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

해당 내용으로 반영한 거 좋네요 ~ 👍

.body("tags.tagName", contains("Java", "4기", "BE", "FE", "React"));
}

@DisplayName("공백의 태그 이름일 경우 전체 태그 목록을 조회한다.")
@Test
void getAllTagsByBlankTagName() {
RestAssured.given().log().all()
.queryParam("tag-name", " \t ")
.when().log().all()
.get("/api/tags")
.then().log().all()
.statusCode(HttpStatus.OK.value())
.body("tags", hasSize(5))
.body("tags.id", not(empty()))
.body("tags.tagName", contains("Java", "4기", "BE", "FE", "React"));
}

@DisplayName("태그 이름을 포함한 태그 목록을 대소문자 구분없이 조회한다.")
@Test
void getTagsByTagName() {
RestAssured.given().log().all()
.queryParam("tag-name", "ja")
.when().log().all()
.get("/api/tags")
.then().log().all()
.statusCode(HttpStatus.OK.value())
.body("tags", hasSize(1))
.body("tags.id", not(empty()))
.body("tags.tagName", contains("Java"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,8 @@ void searchByKeyword() {
@DisplayName("앞뒤 공백을 제거한 문자열로 스터디 목록 조회")
@Test
void searchWithTrimKeyword() {
ResponseEntity<StudiesResponse> response = studyController.searchStudies(" Java 스터디 ", PageRequest.of(0, 3));
ResponseEntity<StudiesResponse> response = studyController
.searchStudies(" Java 스터디 ", PageRequest.of(0, 3));

assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(response.getBody()).isNotNull();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package com.woowacourse.moamoa.tag.controller;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.tuple;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import com.woowacourse.moamoa.tag.domain.Tag;
import com.woowacourse.moamoa.tag.domain.repository.TagRepository;
import com.woowacourse.moamoa.tag.service.TagService;
import com.woowacourse.moamoa.tag.service.response.TagResponse;
import com.woowacourse.moamoa.tag.service.response.TagsResponse;
import java.util.List;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;

class TagControllerTest {

private TagRepository tagRepository;
private TagController tagController;

@BeforeEach
void setUp() {
tagRepository = Mockito.mock(TagRepository.class);
when(tagRepository.findAllByNameContainingIgnoreCase("")).thenReturn(List.of(
Copy link
Collaborator

Choose a reason for hiding this comment

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

한 줄에 점 하나로 하면 보기 편할 것 같아요!

new Tag(1L, "Java"), new Tag(2L, "4기"), new Tag(3L, "BE")
));
when(tagRepository.findAllByNameContainingIgnoreCase("ja")).thenReturn(List.of(
new Tag(1L, "Java")
));
tagController = new TagController(new TagService(tagRepository));
}

@DisplayName("태그 목록 전체를 조회한다.")
@Test
void getTags() {
ResponseEntity<TagsResponse> response = tagController.getTags("");

assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(response.getBody()).isNotNull();
assertThat(response.getBody().getTags())
.extracting(TagResponse::getId, TagResponse::getTagName)
.containsExactly(
tuple(1L, "Java"),
tuple(2L, "4기"),
tuple(3L, "BE")
);

verify(tagRepository).findAllByNameContainingIgnoreCase("");
}

@DisplayName("태그 이름을 대소문자 구분없이 앞뒤 공백을 제거해 태그 목록을 조회한다.")
@Test
void getTagsByName() {
ResponseEntity<TagsResponse> response = tagController.getTags(" ja \t ");

assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(response.getBody()).isNotNull();
assertThat(response.getBody().getTags())
.extracting(TagResponse::getId, TagResponse::getTagName)
.containsExactly(
tuple(1L, "Java")
);

verify(tagRepository).findAllByNameContainingIgnoreCase("ja");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.woowacourse.moamoa.tag.domain.repository;

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

import com.woowacourse.moamoa.tag.domain.Tag;
import java.util.List;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;

@DataJpaTest
class TagRepositoryTest {

@Autowired
private TagRepository tagRepository;

@DisplayName("태그 없이 태그 조회시 태그 목록 전체를 조회한다.")
@Test
void findAllByBlankTagName() {
List<Tag> tags = tagRepository.findAllByNameContainingIgnoreCase("");

assertThat(tags).hasSize(5)
.filteredOn(tag -> tag.getId() != null)
.extracting("name")
.containsExactlyInAnyOrder("Java", "4기", "BE", "FE", "React");
}

@DisplayName("대소문자 구분없이 태그 이름으로 조회한다.")
@Test
void findAllByNameContainingIgnoreCase() {
List<Tag> tags = tagRepository.findAllByNameContainingIgnoreCase("ja");

assertThat(tags).hasSize(1)
.filteredOn(tag -> tag.getId() != null)
.extracting("name")
.containsExactlyInAnyOrder("Java");
}
}