Skip to content
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
18 changes: 18 additions & 0 deletions src/main/java/io/spring/application/ArticleQueryService.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import io.spring.application.data.ArticleDataList;
import io.spring.application.data.ArticleFavoriteCount;
import io.spring.core.user.User;
import io.spring.infrastructure.mybatis.readservice.ArticleBookmarksReadService;
import io.spring.infrastructure.mybatis.readservice.ArticleFavoritesReadService;
import io.spring.infrastructure.mybatis.readservice.ArticleReadService;
import io.spring.infrastructure.mybatis.readservice.UserRelationshipQueryService;
Expand All @@ -26,6 +27,7 @@ public class ArticleQueryService {
private ArticleReadService articleReadService;
private UserRelationshipQueryService userRelationshipQueryService;
private ArticleFavoritesReadService articleFavoritesReadService;
private ArticleBookmarksReadService articleBookmarksReadService;

public Optional<ArticleData> findById(String id, User user) {
ArticleData articleData = articleReadService.findById(id);
Expand Down Expand Up @@ -126,6 +128,7 @@ private void fillExtraInfo(List<ArticleData> articles, User currentUser) {
setFavoriteCount(articles);
if (currentUser != null) {
setIsFavorite(articles, currentUser);
setIsBookmarked(articles, currentUser);
setIsFollowingAuthor(articles, currentUser);
}
}
Expand Down Expand Up @@ -172,8 +175,23 @@ private void setIsFavorite(List<ArticleData> articles, User currentUser) {
});
}

private void setIsBookmarked(List<ArticleData> articles, User currentUser) {
Set<String> bookmarkedArticles =
articleBookmarksReadService.userBookmarks(
articles.stream().map(articleData -> articleData.getId()).collect(toList()),
currentUser);

articles.forEach(
articleData -> {
if (bookmarkedArticles.contains(articleData.getId())) {
articleData.setBookmarked(true);
}
});
}

private void fillExtraInfo(String id, User user, ArticleData articleData) {
articleData.setFavorited(articleFavoritesReadService.isUserFavorite(user.getId(), id));
articleData.setBookmarked(articleBookmarksReadService.isUserBookmark(user.getId(), id));
articleData.setFavoritesCount(articleFavoritesReadService.articleFavoriteCount(id));
articleData
.getProfileData()
Expand Down
1 change: 1 addition & 0 deletions src/main/java/io/spring/application/data/ArticleData.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ public class ArticleData implements io.spring.application.Node {
private String description;
private String body;
private boolean favorited;
private boolean bookmarked;
private int favoritesCount;
private DateTime createdAt;
private DateTime updatedAt;
Expand Down
1 change: 1 addition & 0 deletions src/main/java/io/spring/graphql/ArticleDatafetcher.java
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,7 @@ private Article buildArticleResult(ArticleData articleData) {
.createdAt(ISODateTimeFormat.dateTime().withZoneUTC().print(articleData.getCreatedAt()))
.description(articleData.getDescription())
.favorited(articleData.isFavorited())
.bookmarked(articleData.isBookmarked())
.favoritesCount(articleData.getFavoritesCount())
.slug(articleData.getSlug())
.tagList(articleData.getTagList())
Expand Down
1 change: 1 addition & 0 deletions src/main/resources/schema/schema.graphqls
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ type Article {
createdAt: String!
description: String!
favorited: Boolean!
bookmarked: Boolean!

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

🚩 No bookmark/unbookmark API endpoints exist yet

The PR adds read-side support for bookmarks (querying whether an article is bookmarked, exposing the flag in REST/GraphQL responses), but there are no REST controller endpoints or GraphQL mutations to actually create or remove bookmarks. The domain model (ArticleBookmarkRepository with save/find/remove) and the persistence layer (MyBatisArticleBookmarkRepository, ArticleBookmarkMapper.xml) are fully wired, but without API endpoints, the bookmarked field will always be false in production until a follow-up PR adds the write-side. This may be intentional (incremental delivery), but worth confirming.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Intentional — incremental delivery. MTM-4 owns the shared read-side bookmarked flag (decision D5); the bookmark add/remove write endpoints are delivered by the parallel MTM-2 story and were explicitly out of scope here (the spec said not to depend on them). State is driven via ArticleBookmarkRepository in tests to verify the flag flips. Once MTM-2 is integrated, the flag will reflect real bookmarks end-to-end.

favoritesCount: Int!
slug: String!
tagList: [String],
Expand Down
2 changes: 2 additions & 0 deletions src/test/java/io/spring/TestHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ public static ArticleData articleDataFixture(String seed, User user) {
"desc " + seed,
"body " + seed,
false,
false,
0,
now,
now,
Expand All @@ -33,6 +34,7 @@ public static ArticleData getArticleDataFromArticleAndUser(Article article, User
article.getDescription(),
article.getBody(),
false,
false,
0,
article.getCreatedAt(),
article.getUpdatedAt(),
Expand Down
27 changes: 27 additions & 0 deletions src/test/java/io/spring/api/ArticleApiTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,35 @@ public void should_read_article_success() throws Exception {
.statusCode(200)
.body("article.slug", equalTo(slug))
.body("article.body", equalTo(articleData.getBody()))
.body("article.bookmarked", equalTo(false))
.body("article.createdAt", equalTo(ISODateTimeFormat.dateTime().withZoneUTC().print(time)));
}

@Test
public void should_read_article_with_bookmarked_flag() throws Exception {
String slug = "test-new-article";
Article article =
new Article(
"Test New Article",
"Desc",
"Body",
Arrays.asList("java", "spring", "jpg"),
user.getId(),
new DateTime());
ArticleData articleData = TestHelper.getArticleDataFromArticleAndUser(article, user);
articleData.setBookmarked(true);

when(articleQueryService.findBySlug(eq(slug), eq(user))).thenReturn(Optional.of(articleData));

given()
.header("Authorization", "Token " + token)
.when()
.get("/articles/{slug}", slug)
.then()
.statusCode(200)
.body("article.bookmarked", equalTo(true));
}

@Test
public void should_404_if_article_not_found() throws Exception {
when(articleQueryService.findBySlug(anyString(), any())).thenReturn(Optional.empty());
Expand Down Expand Up @@ -140,6 +166,7 @@ public void should_get_403_if_not_author_to_update_article() throws Exception {
article.getDescription(),
article.getBody(),
false,
false,
0,
time,
time,
Expand Down
1 change: 1 addition & 0 deletions src/test/java/io/spring/api/ArticleFavoriteApiTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ public void setUp() throws Exception {
article.getDescription(),
article.getBody(),
true,
false,
1,
article.getCreatedAt(),
article.getUpdatedAt(),
Expand Down
2 changes: 2 additions & 0 deletions src/test/java/io/spring/api/ArticlesApiTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ public void should_create_article_success() throws Exception {
description,
body,
false,
false,
0,
new DateTime(),
new DateTime(),
Expand Down Expand Up @@ -131,6 +132,7 @@ public void should_get_error_message_with_duplicated_title() {
description,
body,
false,
false,
0,
new DateTime(),
new DateTime(),
Expand Down
26 changes: 19 additions & 7 deletions src/test/java/io/spring/api/ListArticleApiTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import static io.restassured.module.mockmvc.RestAssuredMockMvc.given;
import static io.spring.TestHelper.articleDataFixture;
import static java.util.Arrays.asList;
import static org.hamcrest.core.IsEqual.equalTo;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;

Expand All @@ -12,6 +13,7 @@
import io.spring.application.ArticleQueryService;
import io.spring.application.Page;
import io.spring.application.article.ArticleCommandService;
import io.spring.application.data.ArticleData;
import io.spring.application.data.ArticleDataList;
import io.spring.core.article.ArticleRepository;
import org.junit.jupiter.api.BeforeEach;
Expand Down Expand Up @@ -42,13 +44,20 @@ public void setUp() throws Exception {

@Test
public void should_get_default_article_list() throws Exception {
ArticleDataList articleDataList =
new ArticleDataList(
asList(articleDataFixture("1", user), articleDataFixture("2", user)), 2);
ArticleData bookmarked = articleDataFixture("1", user);
bookmarked.setBookmarked(true);
ArticleData notBookmarked = articleDataFixture("2", user);
ArticleDataList articleDataList = new ArticleDataList(asList(bookmarked, notBookmarked), 2);
when(articleQueryService.findRecentArticles(
eq(null), eq(null), eq(null), eq(new Page(0, 20)), eq(null)))
.thenReturn(articleDataList);
RestAssuredMockMvc.when().get("/articles").prettyPeek().then().statusCode(200);
RestAssuredMockMvc.when()
.get("/articles")
.prettyPeek()
.then()
.statusCode(200)
.body("articles[0].bookmarked", equalTo(true))
.body("articles[1].bookmarked", equalTo(false));
}

@Test
Expand All @@ -58,9 +67,10 @@ public void should_get_feeds_401_without_login() throws Exception {

@Test
public void should_get_feeds_success() throws Exception {
ArticleData bookmarked = articleDataFixture("1", user);
bookmarked.setBookmarked(true);
ArticleDataList articleDataList =
new ArticleDataList(
asList(articleDataFixture("1", user), articleDataFixture("2", user)), 2);
new ArticleDataList(asList(bookmarked, articleDataFixture("2", user)), 2);
when(articleQueryService.findUserFeed(eq(user), eq(new Page(0, 20))))
.thenReturn(articleDataList);

Expand All @@ -70,6 +80,8 @@ public void should_get_feeds_success() throws Exception {
.get("/articles/feed")
.prettyPeek()
.then()
.statusCode(200);
.statusCode(200)
.body("articles[0].bookmarked", equalTo(true))
.body("articles[1].bookmarked", equalTo(false));
}
}
Loading
Loading