Skip to content

Commit aff39a4

Browse files
authored
Merge pull request #93 from YAPP-Github/feat/PRODUCT-190
[Feat] 가게의 담긴 이야기 기능 구현
2 parents 723fde1 + 495a856 commit aff39a4

File tree

13 files changed

+292
-1
lines changed

13 files changed

+292
-1
lines changed
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package eatda.controller.article;
2+
3+
import eatda.service.article.ArticleService;
4+
import jakarta.validation.constraints.Max;
5+
import jakarta.validation.constraints.Min;
6+
import lombok.RequiredArgsConstructor;
7+
import org.springframework.http.HttpStatus;
8+
import org.springframework.http.ResponseEntity;
9+
import org.springframework.stereotype.Controller;
10+
import org.springframework.web.bind.annotation.GetMapping;
11+
import org.springframework.web.bind.annotation.RequestParam;
12+
13+
@Controller
14+
@RequiredArgsConstructor
15+
public class ArticleController {
16+
17+
private final ArticleService articleService;
18+
19+
@GetMapping("/api/articles")
20+
public ResponseEntity<ArticlesResponse> getArticles(@RequestParam(defaultValue = "3") @Min(1) @Max(50) int size) {
21+
return ResponseEntity.status(HttpStatus.OK)
22+
.body(articleService.getAllArticles(size));
23+
}
24+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package eatda.controller.article;
2+
3+
public record ArticleResponse(
4+
String title,
5+
String subtitle,
6+
String articleUrl,
7+
String imageUrl
8+
) {
9+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package eatda.controller.article;
2+
3+
import java.util.List;
4+
5+
public record ArticlesResponse(
6+
List<ArticleResponse> articles
7+
) {
8+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package eatda.repository.article;
2+
3+
import eatda.domain.article.Article;
4+
import org.springframework.data.domain.Page;
5+
import org.springframework.data.domain.Pageable;
6+
import org.springframework.data.jpa.repository.JpaRepository;
7+
8+
public interface ArticleRepository extends JpaRepository<Article, Long> {
9+
10+
Page<Article> findAllByOrderByCreatedAtDesc(Pageable pageable);
11+
12+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package eatda.service.article;
2+
3+
import eatda.controller.article.ArticleResponse;
4+
import eatda.controller.article.ArticlesResponse;
5+
import eatda.repository.article.ArticleRepository;
6+
import eatda.service.common.ImageService;
7+
import java.util.List;
8+
import lombok.RequiredArgsConstructor;
9+
import org.springframework.data.domain.PageRequest;
10+
import org.springframework.stereotype.Service;
11+
12+
@Service
13+
@RequiredArgsConstructor
14+
public class ArticleService {
15+
16+
private final ArticleRepository articleRepository;
17+
private final ImageService imageService;
18+
19+
public ArticlesResponse getAllArticles(int size) {
20+
PageRequest pageRequest = PageRequest.of(0, size);
21+
List<ArticleResponse> articles = articleRepository.findAllByOrderByCreatedAtDesc(pageRequest)
22+
.stream()
23+
.map(article -> new ArticleResponse(
24+
article.getTitle(),
25+
article.getSubtitle(),
26+
article.getArticleUrl(),
27+
imageService.getPresignedUrl(article.getImageKey())
28+
))
29+
.toList();
30+
31+
return new ArticlesResponse(articles);
32+
}
33+
}

src/test/java/eatda/controller/BaseControllerTest.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,14 @@
1212
import eatda.client.oauth.OauthToken;
1313
import eatda.controller.web.jwt.JwtManager;
1414
import eatda.domain.member.Member;
15+
import eatda.fixture.ArticleGenerator;
1516
import eatda.fixture.CheerGenerator;
1617
import eatda.fixture.MemberGenerator;
1718
import eatda.fixture.StoreGenerator;
1819
import eatda.repository.member.MemberRepository;
1920
import eatda.repository.store.CheerRepository;
2021
import eatda.repository.store.StoreRepository;
2122
import eatda.service.common.ImageService;
22-
import eatda.service.common.ImageService;
2323
import eatda.service.story.StoryService;
2424
import io.restassured.RestAssured;
2525
import io.restassured.builder.RequestSpecBuilder;
@@ -59,6 +59,9 @@ public class BaseControllerTest {
5959
@Autowired
6060
protected CheerGenerator cheerGenerator;
6161

62+
@Autowired
63+
protected ArticleGenerator articleGenerator;
64+
6265
@Autowired
6366
protected MemberRepository memberRepository;
6467

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package eatda.controller.article;
2+
3+
import static org.assertj.core.api.Assertions.assertThat;
4+
5+
import eatda.controller.BaseControllerTest;
6+
import eatda.domain.article.Article;
7+
import org.junit.jupiter.api.Nested;
8+
import org.junit.jupiter.api.Test;
9+
10+
public class ArticleControllerTest extends BaseControllerTest {
11+
12+
@Nested
13+
class GetArticles {
14+
15+
@Test
16+
void 가게의_담긴_이야기_목록을_조회할_수_있다() {
17+
Article article1 = articleGenerator.generate("국밥의 모든 것");
18+
Article article2 = articleGenerator.generate("순대국의 진실");
19+
20+
ArticlesResponse response = given()
21+
.queryParam("size", 3)
22+
.when()
23+
.get("/api/articles")
24+
.then().statusCode(200)
25+
.extract().as(ArticlesResponse.class);
26+
27+
assertThat(response.articles()).hasSize(2);
28+
assertThat(response.articles().getFirst().title()).isEqualTo("순대국의 진실");
29+
}
30+
}
31+
}

src/test/java/eatda/document/BaseDocumentTest.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import eatda.controller.web.jwt.JwtManager;
88
import eatda.exception.BusinessErrorCode;
99
import eatda.exception.EtcErrorCode;
10+
import eatda.service.article.ArticleService;
1011
import eatda.service.auth.AuthService;
1112
import eatda.service.common.ImageService;
1213
import eatda.service.member.MemberService;
@@ -57,6 +58,10 @@ public abstract class BaseDocumentTest {
5758

5859
@MockitoBean
5960
protected CheerService cheerService;
61+
62+
@MockitoBean
63+
protected ArticleService articleService;
64+
6065
@MockitoBean
6166
protected JwtManager jwtManager;
6267

src/test/java/eatda/document/Tag.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ public enum Tag {
77
STORE_API("Store API"),
88
STORY_API("Story API"),
99
CHEER_API("Cheer API"),
10+
ARTICLE_API("Article API"),
1011
;
1112

1213
private final String displayName;
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
package eatda.document.article;
2+
3+
import static org.mockito.Mockito.doReturn;
4+
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
5+
import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName;
6+
7+
import eatda.controller.article.ArticleResponse;
8+
import eatda.controller.article.ArticlesResponse;
9+
import eatda.document.BaseDocumentTest;
10+
import eatda.document.RestDocsRequest;
11+
import eatda.document.RestDocsResponse;
12+
import eatda.document.Tag;
13+
import io.restassured.response.Response;
14+
import java.util.List;
15+
import org.junit.jupiter.api.Nested;
16+
import org.junit.jupiter.api.Test;
17+
import org.springframework.restdocs.restassured.RestDocumentationFilter;
18+
19+
public class ArticleDocumentTest extends BaseDocumentTest {
20+
21+
@Nested
22+
class GetArticles {
23+
24+
RestDocsRequest requestDocument = request()
25+
.tag(Tag.ARTICLE_API)
26+
.summary("가게의 담긴 이야기")
27+
.description("게시글을 최신순으로 페이지네이션하여 조회합니다.")
28+
.queryParameter(parameterWithName("size").description("페이지당 조회할 아티클 개수 (default = 3)"));
29+
30+
RestDocsResponse responseDocument = response()
31+
.responseBodyField(
32+
fieldWithPath("articles").description("게시글 응답 리스트"),
33+
fieldWithPath("articles[].title").description("게시글 제목"),
34+
fieldWithPath("articles[].subtitle").description("게시글 소제목"),
35+
fieldWithPath("articles[].articleUrl").description("게시글 링크 URL"),
36+
fieldWithPath("articles[].imageUrl").description("게시글 이미지 URL")
37+
);
38+
39+
@Test
40+
void 가게의_담긴_이야기_목록_조회_성공() {
41+
ArticlesResponse mockResponse = new ArticlesResponse(List.of(
42+
new ArticleResponse(
43+
"국밥의 모든 것",
44+
"뜨끈한 국물의 세계",
45+
"https://eatda.com/article/1",
46+
"https://s3.bucket.com/article/1.jpg"
47+
),
48+
new ArticleResponse(
49+
"순대국의 진실",
50+
"돼지부속의 미학",
51+
"https://eatda.com/article/2",
52+
"https://s3.bucket.com/article/2.jpg"
53+
)
54+
));
55+
56+
doReturn(mockResponse)
57+
.when(articleService)
58+
.getAllArticles(3);
59+
60+
RestDocumentationFilter document = document("article/get-articles", 200)
61+
.request(requestDocument)
62+
.response(responseDocument)
63+
.build();
64+
65+
Response response = given(document)
66+
.queryParam("size", 3)
67+
.when()
68+
.get("/api/articles");
69+
70+
response.then()
71+
.statusCode(200);
72+
}
73+
}
74+
}

0 commit comments

Comments
 (0)