Skip to content

feat: add bookmark article feature#146

Open
devin-ai-integration[bot] wants to merge 5 commits into
mainfrom
devin/1781526846-feature-bookmark-article
Open

feat: add bookmark article feature#146
devin-ai-integration[bot] wants to merge 5 commits into
mainfrom
devin/1781526846-feature-bookmark-article

Conversation

@devin-ai-integration

@devin-ai-integration devin-ai-integration Bot commented Jun 15, 2026

Copy link
Copy Markdown

Summary

Adds a complete "bookmark article" feature — a personal reading list separate from the public favorites system. Users can bookmark/unbookmark articles via REST, GraphQL, and the frontend UI.

Backend (write-side):

  • ArticleBookmark domain entity with composite key (articleId, userId)
  • MyBatis mapper + Flyway V3 migration for article_bookmarks table
  • REST: POST/DELETE /articles/{slug}/bookmark (mirrors ArticleFavoriteApi pattern)
  • GraphQL: bookmarkArticle(slug) / unbookmarkArticle(slug) mutations

Backend (read-side):

  • ArticleBookmarksReadService mapper with isUserBookmark() + batch userBookmarks()
  • ArticleQueryService.fillExtraInfo() sets bookmarked field on ArticleData
  • bookmarked: Boolean! exposed on GraphQL Article type

Frontend:

  • ArticleAPI.bookmark(slug, token) / unbookmark(slug, token) API methods
  • Bookmark button on article detail page (ArticleMeta.tsx): toggles "Bookmark" ↔ "Bookmarked"
  • Bookmark icon on article list cards (ArticlePreview.tsx): ion-bookmark next to existing ion-heart
  • Optimistic UI updates with error rollback

Tests: 7 new (4 API + 3 repository), all 75 tests passing. Verification gate: ./gradlew clean test spotlessCheck — BUILD SUCCESSFUL.

Link to Devin session: https://partner-workshops.devinenterprise.com/sessions/34e35171066542b98392453ed8d49049
Requested by: @bsmitches


Open in Devin Review

- Domain entity: ArticleBookmark (articleId, userId, createdAt)
- Flyway migration V3 for article_bookmarks table
- MyBatis mapper + XML for bookmark CRUD
- Repository implementation: MyBatisArticleBookmarkRepository
- REST API: POST/DELETE /articles/{slug}/bookmark
- GraphQL mutations: bookmarkArticle / unbookmarkArticle
- Unit tests: API layer (4 tests) + repository layer (3 tests)
@devin-ai-integration

Copy link
Copy Markdown
Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

devin-ai-integration[bot]

This comment was marked as resolved.

- Add bookmarked field to ArticleData
- Add ArticleBookmarksReadService (MyBatis mapper for bookmark queries)
- Integrate into ArticleQueryService.fillExtraInfo() (single + batch)
- Add bookmarked field to GraphQL Article type
- Wire bookmarked into ArticleDatafetcher.buildArticleResult()
- Update all test constructor calls for new ArticleData field
- Add bookmark/unbookmark API methods to ArticleAPI
- Add bookmark button (ion-bookmark icon) to ArticleMeta on detail page
  Shows 'Bookmark'/'Bookmarked' text with toggle on click
- Add bookmark icon button to ArticlePreview cards on article list
  Sits alongside existing favorite heart button
- Both buttons use optimistic UI updates with error rollback

@devin-ai-integration devin-ai-integration Bot left a comment

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.

Devin Review found 4 new potential issues.

Open in Devin Review

@devin-ai-integration devin-ai-integration Bot Jun 15, 2026

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.

🚩 Bookmark error handler in ArticlePreview is more correct than the existing favorite error handler

The new handleClickBookmark error handler at line 48-51 uses bookmarked: preview.bookmarked (the original closure value) to revert state on API failure — this is correct. Interestingly, the pre-existing handleClickFavorite error handler at frontend/components/article/ArticlePreview.tsx:88-94 uses favorited: !preview.favorited which reapplies the toggled value rather than reverting it, effectively making the catch block a no-op for the favorited field. This is a pre-existing bug in the favorite handler that the bookmark handler avoids. Consider fixing handleClickFavorite's error handler to match the bookmark pattern.

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.

Good catch — the same stale-closure pattern exists in the pre-existing handleClickFavorite. That's a pre-existing bug outside the scope of this PR's bookmark feature, but worth noting for a follow-up fix.

Comment thread frontend/components/article/ArticleMeta.tsx Outdated
Comment thread frontend/components/article/ArticlePreview.tsx
Comment on lines 35 to +38
MyBatisArticleRepository.class,
MyBatisArticleFavoriteRepository.class
})
@Sql("classpath:create_article_bookmarks.sql")

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.

🚩 ArticleQueryServiceTest may fail due to missing ArticleBookmarksReadService bean

The ArticleQueryService class now has a new ArticleBookmarksReadService dependency injected via @AllArgsConstructor (src/main/java/io/spring/application/ArticleQueryService.java:30). However, ArticleQueryServiceTest at src/test/java/io/spring/application/article/ArticleQueryServiceTest.java:32-37 uses @Import to specify exact beans and does not include MyBatisArticleBookmarkRepository or anything that provides ArticleBookmarksReadService. It does add @Sql("classpath:create_article_bookmarks.sql") for the table, but the ArticleBookmarksReadService MyBatis mapper bean may or may not be auto-scanned by the test context. If it's not auto-scanned, the test will fail at context startup with a missing bean error. This depends on how the test's @MapperScan or component scan is configured in DbTestBase.

(Refers to lines 32-38)

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.

The ArticleBookmarksReadService mapper is auto-scanned by the test context's @MapperScan configured in DbTestBase. All 75 tests pass including ArticleQueryServiceTest — verified locally and in CI.

The catch blocks used !bookmarked / !preview.bookmarked which references
the stale pre-update closure value, resulting in no actual revert on error.
Changed to bookmarked / preview.bookmarked to restore the original state.
The external CSS at demo.productionready.io/main.css now returns 404,
leaving the entire frontend unstyled. Vendor the CSS into public/main.css
(sourced from Web Archive) and update _document.tsx to load it locally.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant