Conversation
WalkthroughA new feature was implemented to allow retrieval of articles filtered by category, including pagination and sorting by newest first. This involved adding a new endpoint, service, repository methods, and a corresponding test to ensure articles can be fetched by category with the correct count and order. Additionally, unread article counts were integrated into responses and DTOs were extended to include creation timestamps. Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant Controller
participant Usecase
participant Service
participant Repository
Client->>Controller: GET /articles/{categoryId}?pageNumber=&pageSize=
Controller->>Usecase: getAllArticlesByCategory(user, categoryId, pageNumber, pageSize)
Usecase->>Service: findAllByCategory(userId, category, pageRequest)
Service->>Repository: findAllByCategory(userId, categoryId, pageable)
Repository-->>Service: ArticlesWithUnreadCount (page + unread count)
Service-->>Usecase: ArticlesWithUnreadCount
Usecase-->>Controller: ArticleAllResponse (total, unread, articles)
Controller-->>Client: ResponseDto<ArticleAllResponse>
Assessment against linked issues
Assessment against linked issues: Out-of-scope changes
Possibly related PRs
Suggested reviewers
Poem
✨ Finishing Touches
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
src/main/java/com/pinback/pinback_server/domain/article/domain/repository/ArticleRepositoryCustom.java (1)
13-13: Fix parameter naming for clarity.The parameter
articleIdshould be renamed tocategoryIdto accurately reflect its purpose and maintain consistency with how it's used in the service layer.- Page<Article> findAllByCategory(UUID userId, long articleId, Pageable pageable); + Page<Article> findAllByCategory(UUID userId, long categoryId, Pageable pageable);
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
src/main/java/com/pinback/pinback_server/domain/article/application/ArticleManagementUsecase.java(1 hunks)src/main/java/com/pinback/pinback_server/domain/article/domain/repository/ArticleRepositoryCustom.java(1 hunks)src/main/java/com/pinback/pinback_server/domain/article/domain/repository/ArticleRepositoryCustomImpl.java(3 hunks)src/main/java/com/pinback/pinback_server/domain/article/domain/service/ArticleGetService.java(2 hunks)src/main/java/com/pinback/pinback_server/domain/article/presentation/ArticleController.java(1 hunks)src/test/java/com/pinback/pinback_server/domain/article/application/ArticleManagementUsecaseTest.java(1 hunks)
🧰 Additional context used
🧠 Learnings (6)
📓 Common learnings
Learnt from: rootTiket
PR: Pinback-Team/pinback-server#18
File: src/main/java/com/pinback/pinback_server/domain/article/application/ArticleManagementUsecase.java:38-41
Timestamp: 2025-07-07T15:33:02.697Z
Learning: In the Pinback server project, article detail retrieval (getArticleDetail method) does not require user authorization checks. Articles can be accessed by ID without ownership verification as per the project's design decision.
src/main/java/com/pinback/pinback_server/domain/article/domain/repository/ArticleRepositoryCustom.java (1)
Learnt from: rootTiket
PR: Pinback-Team/pinback-server#18
File: src/main/java/com/pinback/pinback_server/domain/article/application/ArticleManagementUsecase.java:38-41
Timestamp: 2025-07-07T15:33:02.697Z
Learning: In the Pinback server project, article detail retrieval (getArticleDetail method) does not require user authorization checks. Articles can be accessed by ID without ownership verification as per the project's design decision.
src/main/java/com/pinback/pinback_server/domain/article/domain/service/ArticleGetService.java (1)
Learnt from: rootTiket
PR: Pinback-Team/pinback-server#18
File: src/main/java/com/pinback/pinback_server/domain/article/application/ArticleManagementUsecase.java:38-41
Timestamp: 2025-07-07T15:33:02.697Z
Learning: In the Pinback server project, article detail retrieval (getArticleDetail method) does not require user authorization checks. Articles can be accessed by ID without ownership verification as per the project's design decision.
src/main/java/com/pinback/pinback_server/domain/article/application/ArticleManagementUsecase.java (1)
Learnt from: rootTiket
PR: Pinback-Team/pinback-server#18
File: src/main/java/com/pinback/pinback_server/domain/article/application/ArticleManagementUsecase.java:38-41
Timestamp: 2025-07-07T15:33:02.697Z
Learning: In the Pinback server project, article detail retrieval (getArticleDetail method) does not require user authorization checks. Articles can be accessed by ID without ownership verification as per the project's design decision.
src/main/java/com/pinback/pinback_server/domain/article/presentation/ArticleController.java (1)
Learnt from: rootTiket
PR: Pinback-Team/pinback-server#18
File: src/main/java/com/pinback/pinback_server/domain/article/application/ArticleManagementUsecase.java:38-41
Timestamp: 2025-07-07T15:33:02.697Z
Learning: In the Pinback server project, article detail retrieval (getArticleDetail method) does not require user authorization checks. Articles can be accessed by ID without ownership verification as per the project's design decision.
src/main/java/com/pinback/pinback_server/domain/article/domain/repository/ArticleRepositoryCustomImpl.java (1)
Learnt from: rootTiket
PR: Pinback-Team/pinback-server#18
File: src/main/java/com/pinback/pinback_server/domain/article/application/ArticleManagementUsecase.java:38-41
Timestamp: 2025-07-07T15:33:02.697Z
Learning: In the Pinback server project, article detail retrieval (getArticleDetail method) does not require user authorization checks. Articles can be accessed by ID without ownership verification as per the project's design decision.
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build
🔇 Additional comments (5)
src/main/java/com/pinback/pinback_server/domain/article/domain/service/ArticleGetService.java (2)
13-13: LGTM: Clean import addition.The Category entity import is necessary for the new method signature.
36-38: LGTM: Clean service method implementation.The method correctly delegates to the repository layer and properly extracts the category ID from the Category entity. The implementation follows the established pattern in this service class.
src/test/java/com/pinback/pinback_server/domain/article/application/ArticleManagementUsecaseTest.java (1)
144-174: LGTM: Comprehensive test coverage for category-based article retrieval.The test thoroughly validates the new functionality:
- Proper filtering by category
- Correct pagination behavior
- Descending order sorting by article ID
- Accurate total count calculation
The test setup and assertions are well-structured and follow the established patterns in the test class.
src/main/java/com/pinback/pinback_server/domain/article/application/ArticleManagementUsecase.java (1)
62-77: LGTM: Well-structured use case implementation.The method correctly implements the category-based article retrieval with proper:
- User authorization through
categoryGetService.getCategoryAndUser- Delegation to service layer
- Response mapping following established patterns
- Consistent structure with the existing
getAllArticlesmethodsrc/main/java/com/pinback/pinback_server/domain/article/domain/repository/ArticleRepositoryCustomImpl.java (1)
4-4: LGTM: Required imports for the new functionalityThe static import for
QCategoryand the import forBooleanExpressionare correctly added to support the new category-based filtering method.Also applies to: 16-16
| @GetMapping("/{categoryId}") | ||
| public ResponseDto<ArticleAllResponse> getAllByCategory(@CurrentUser User user, @RequestParam Long categoryId, | ||
| @RequestParam int pageNumber, | ||
| @RequestParam int pageSize) { | ||
|
|
||
| ArticleAllResponse response = articleManagementUsecase.getAllArticlesByCategory(user, categoryId, pageNumber, | ||
| pageSize); | ||
| return ResponseDto.ok(response); | ||
| } |
There was a problem hiding this comment.
Fix annotation mismatch for categoryId parameter.
The endpoint path uses {categoryId} as a path variable, but the parameter is annotated with @RequestParam. This will cause the endpoint to fail at runtime.
- public ResponseDto<ArticleAllResponse> getAllByCategory(@CurrentUser User user, @RequestParam Long categoryId,
+ public ResponseDto<ArticleAllResponse> getAllByCategory(@CurrentUser User user, @PathVariable Long categoryId,📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| @GetMapping("/{categoryId}") | |
| public ResponseDto<ArticleAllResponse> getAllByCategory(@CurrentUser User user, @RequestParam Long categoryId, | |
| @RequestParam int pageNumber, | |
| @RequestParam int pageSize) { | |
| ArticleAllResponse response = articleManagementUsecase.getAllArticlesByCategory(user, categoryId, pageNumber, | |
| pageSize); | |
| return ResponseDto.ok(response); | |
| } | |
| @GetMapping("/{categoryId}") | |
| public ResponseDto<ArticleAllResponse> getAllByCategory(@CurrentUser User user, @PathVariable Long categoryId, | |
| @RequestParam int pageNumber, | |
| @RequestParam int pageSize) { | |
| ArticleAllResponse response = articleManagementUsecase.getAllArticlesByCategory(user, categoryId, pageNumber, | |
| pageSize); | |
| return ResponseDto.ok(response); | |
| } |
🤖 Prompt for AI Agents
In
src/main/java/com/pinback/pinback_server/domain/article/presentation/ArticleController.java
around lines 48 to 56, the categoryId parameter is incorrectly annotated with
@RequestParam while the endpoint path uses {categoryId} as a path variable.
Change the annotation on the categoryId parameter from @RequestParam to
@PathVariable to match the path variable in the URL and fix the runtime failure.
| @Override | ||
| public Page<Article> findAllByCategory(UUID userId, long categoryId, Pageable pageable) { | ||
|
|
||
| BooleanExpression conditions = article.user.id.eq(userId) | ||
| .and(article.category.id.eq(categoryId)); | ||
|
|
||
| List<Article> articles = queryFactory | ||
| .selectFrom(article) | ||
| .join(article.user, user).fetchJoin() | ||
| .join(article.category, category).fetchJoin() | ||
| .where(conditions) | ||
| .offset(pageable.getOffset()) | ||
| .limit(pageable.getPageSize()) | ||
| .orderBy(article.createdAt.desc()) | ||
| .fetch(); | ||
|
|
||
| JPAQuery<Long> countQuery = queryFactory | ||
| .select(article.count()) | ||
| .from(article) | ||
| .where(article.user.id.eq(userId)); | ||
|
|
||
| return PageableExecutionUtils.getPage(articles, pageable, countQuery::fetchOne); | ||
| } |
There was a problem hiding this comment.
Critical bug: Count query doesn't filter by categoryId
The count query on lines 63-66 only filters by userId but omits the categoryId condition, while the main query filters by both. This inconsistency will cause incorrect pagination behavior as the total count will include all user articles, not just those in the specified category.
Apply this fix to include the categoryId filter in the count query:
JPAQuery<Long> countQuery = queryFactory
.select(article.count())
.from(article)
- .where(article.user.id.eq(userId));
+ .where(conditions);This ensures both queries use the same filtering conditions for consistent pagination results.
🤖 Prompt for AI Agents
In
src/main/java/com/pinback/pinback_server/domain/article/domain/repository/ArticleRepositoryCustomImpl.java
between lines 47 and 69, the count query only filters by userId but misses
filtering by categoryId, causing incorrect pagination counts. Fix this by adding
the categoryId condition to the count query's where clause so it matches the
main query's filtering criteria, ensuring consistent and accurate pagination
results.
There was a problem hiding this comment.
@rootTiket 아, 조건을 변수로 뺐지만 count 쿼리에서는 아직 사용하지 않으셨네요!
맞습니다, conditions 변수를 만드셨지만 count 쿼리에서는 여전히 article.user.id.eq(userId)만 사용하고 있어서 카테고리 필터링이 빠져있습니다.
이렇게 수정하시면 됩니다:
JPAQuery<Long> countQuery = queryFactory
.select(article.count())
.from(article)
.where(conditions); // userId와 categoryId 모두 포함이제 메인 쿼리와 count 쿼리가 동일한 조건을 사용해서 정확한 페이징 결과를 얻을 수 있습니다.
🧠 Learnings used
Learnt from: rootTiket
PR: Pinback-Team/pinback-server#18
File: src/main/java/com/pinback/pinback_server/domain/article/application/ArticleManagementUsecase.java:38-41
Timestamp: 2025-07-07T15:33:02.697Z
Learning: In the Pinback server project, article detail retrieval (getArticleDetail method) does not require user authorization checks. Articles can be accessed by ID without ownership verification as per the project's design decision.
응답시 작성 일자가 추가되도록 변경 응답시 읽지 않은 아티클의 개수 추가
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (2)
src/main/java/com/pinback/pinback_server/domain/article/domain/repository/ArticleRepositoryCustom.java (1)
12-12: Fix misleading parameter name in method signature.The parameter
articleIdin thefindAllByCategorymethod is misleading. Based on the method name and context, this should becategoryIdto clearly indicate it's used for filtering by category.Apply this diff to improve clarity:
- ArticlesWithUnreadCount findAllByCategory(UUID userId, long articleId, Pageable pageable); + ArticlesWithUnreadCount findAllByCategory(UUID userId, long categoryId, Pageable pageable);src/test/java/com/pinback/pinback_server/domain/article/application/ArticleManagementUsecaseTest.java (1)
205-207: Fix duplicate display nameBoth
getByCategoryandgetByCategoryWithUnreadCounttests have the same display name, which could cause confusion in test reports.-@DisplayName("카테고리 별로 게시글을 조회할 수 있다.") +@DisplayName("카테고리 별로 게시글을 조회할 수 있고 읽지 않은 게시글 수를 확인할 수 있다.")
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (10)
src/main/java/com/pinback/pinback_server/domain/article/application/ArticleManagementUsecase.java(2 hunks)src/main/java/com/pinback/pinback_server/domain/article/domain/entity/Article.java(1 hunks)src/main/java/com/pinback/pinback_server/domain/article/domain/repository/ArticleRepositoryCustom.java(1 hunks)src/main/java/com/pinback/pinback_server/domain/article/domain/repository/ArticleRepositoryCustomImpl.java(3 hunks)src/main/java/com/pinback/pinback_server/domain/article/domain/repository/dto/ArticlesWithUnreadCount.java(1 hunks)src/main/java/com/pinback/pinback_server/domain/article/domain/service/ArticleGetService.java(2 hunks)src/main/java/com/pinback/pinback_server/domain/article/presentation/dto/response/ArticleAllResponse.java(1 hunks)src/main/java/com/pinback/pinback_server/domain/article/presentation/dto/response/ArticlesResponse.java(1 hunks)src/test/java/com/pinback/pinback_server/domain/article/application/ArticleManagementUsecaseTest.java(2 hunks)src/test/java/com/pinback/pinback_server/domain/fixture/TestFixture.java(1 hunks)
✅ Files skipped from review due to trivial changes (1)
- src/main/java/com/pinback/pinback_server/domain/article/domain/repository/dto/ArticlesWithUnreadCount.java
🚧 Files skipped from review as they are similar to previous changes (1)
- src/main/java/com/pinback/pinback_server/domain/article/application/ArticleManagementUsecase.java
🧰 Additional context used
🧠 Learnings (6)
📓 Common learnings
Learnt from: rootTiket
PR: Pinback-Team/pinback-server#18
File: src/main/java/com/pinback/pinback_server/domain/article/application/ArticleManagementUsecase.java:38-41
Timestamp: 2025-07-07T15:33:02.697Z
Learning: In the Pinback server project, article detail retrieval (getArticleDetail method) does not require user authorization checks. Articles can be accessed by ID without ownership verification as per the project's design decision.
src/main/java/com/pinback/pinback_server/domain/article/domain/entity/Article.java (1)
Learnt from: rootTiket
PR: Pinback-Team/pinback-server#18
File: src/main/java/com/pinback/pinback_server/domain/article/application/ArticleManagementUsecase.java:38-41
Timestamp: 2025-07-07T15:33:02.697Z
Learning: In the Pinback server project, article detail retrieval (getArticleDetail method) does not require user authorization checks. Articles can be accessed by ID without ownership verification as per the project's design decision.
src/main/java/com/pinback/pinback_server/domain/article/domain/repository/ArticleRepositoryCustom.java (1)
Learnt from: rootTiket
PR: Pinback-Team/pinback-server#18
File: src/main/java/com/pinback/pinback_server/domain/article/application/ArticleManagementUsecase.java:38-41
Timestamp: 2025-07-07T15:33:02.697Z
Learning: In the Pinback server project, article detail retrieval (getArticleDetail method) does not require user authorization checks. Articles can be accessed by ID without ownership verification as per the project's design decision.
src/main/java/com/pinback/pinback_server/domain/article/domain/service/ArticleGetService.java (1)
Learnt from: rootTiket
PR: Pinback-Team/pinback-server#18
File: src/main/java/com/pinback/pinback_server/domain/article/application/ArticleManagementUsecase.java:38-41
Timestamp: 2025-07-07T15:33:02.697Z
Learning: In the Pinback server project, article detail retrieval (getArticleDetail method) does not require user authorization checks. Articles can be accessed by ID without ownership verification as per the project's design decision.
src/test/java/com/pinback/pinback_server/domain/article/application/ArticleManagementUsecaseTest.java (1)
Learnt from: rootTiket
PR: Pinback-Team/pinback-server#18
File: src/main/java/com/pinback/pinback_server/domain/article/application/ArticleManagementUsecase.java:38-41
Timestamp: 2025-07-07T15:33:02.697Z
Learning: In the Pinback server project, article detail retrieval (getArticleDetail method) does not require user authorization checks. Articles can be accessed by ID without ownership verification as per the project's design decision.
src/main/java/com/pinback/pinback_server/domain/article/domain/repository/ArticleRepositoryCustomImpl.java (1)
Learnt from: rootTiket
PR: Pinback-Team/pinback-server#18
File: src/main/java/com/pinback/pinback_server/domain/article/application/ArticleManagementUsecase.java:38-41
Timestamp: 2025-07-07T15:33:02.697Z
Learning: In the Pinback server project, article detail retrieval (getArticleDetail method) does not require user authorization checks. Articles can be accessed by ID without ownership verification as per the project's design decision.
🧬 Code Graph Analysis (1)
src/main/java/com/pinback/pinback_server/domain/article/domain/service/ArticleGetService.java (1)
src/main/java/com/pinback/pinback_server/domain/article/exception/ArticleNotFoundException.java (1)
ArticleNotFoundException(6-10)
🔇 Additional comments (12)
src/main/java/com/pinback/pinback_server/domain/article/domain/entity/Article.java (1)
75-81: LGTM! Clean implementation of read status management methods.The
toRead()andtoUnRead()methods provide a clear and convenient API for managing article read status. The implementation is straightforward and consistent with the existing code style.src/test/java/com/pinback/pinback_server/domain/fixture/TestFixture.java (1)
37-41: LGTM! Useful test utility for creating read articles.The
readArticle()method provides a convenient way to create articles marked as read for testing scenarios. The implementation correctly leverages the newtoRead()method and follows the existing pattern of other article creation methods in the fixture.src/main/java/com/pinback/pinback_server/domain/article/presentation/dto/response/ArticlesResponse.java (1)
3-3: LGTM! Clean extension of DTO to include creation timestamp.The addition of the
createdAtfield is properly implemented with the correct import, field definition, and factory method update. The changes are consistent and complete.Also applies to: 11-11, 19-19
src/main/java/com/pinback/pinback_server/domain/article/presentation/dto/response/ArticleAllResponse.java (1)
7-7: LGTM! Proper extension of DTO to include unread article count.The addition of the
totalUnreadArticlefield is correctly implemented with consistent updates to the factory method signature and constructor call. The changes are complete and maintain the DTO's integrity.Also applies to: 10-10, 13-13
src/main/java/com/pinback/pinback_server/domain/article/domain/repository/ArticleRepositoryCustom.java (1)
7-7: LGTM! Proper interface update for unread count integration.The import addition and return type change from
Page<Article>toArticlesWithUnreadCountcorrectly support the new functionality for including unread article counts in the response.Also applies to: 10-10
src/main/java/com/pinback/pinback_server/domain/article/domain/service/ArticleGetService.java (3)
11-11: LGTM: Import statements properly addedThe new imports for
ArticlesWithUnreadCountandCategoryare necessary for the updated method signatures and new functionality.Also applies to: 13-13
32-34: LGTM: Method signature updated to return enhanced DTOThe change from
Page<Article>toArticlesWithUnreadCountaligns with the feature requirements to include unread article counts in the response.
36-38: LGTM: New method for category-based article retrievalThe new
findAllByCategorymethod properly delegates to the repository layer and follows the same pattern as the existingfindAllmethod.src/main/java/com/pinback/pinback_server/domain/article/domain/repository/ArticleRepositoryCustomImpl.java (1)
28-51: LGTM: Enhanced method to include unread countThe
findAllCustommethod has been properly updated to include an unread count query and return the newArticlesWithUnreadCountDTO. All queries correctly filter byuserIdto ensure users only see their own articles.src/test/java/com/pinback/pinback_server/domain/article/application/ArticleManagementUsecaseTest.java (3)
119-119: LGTM: Added assertion for createdAt fieldThe assertion verifies that the
createdAttimestamp is properly populated in the response, which aligns with the DTO enhancements.
145-171: LGTM: Comprehensive test for unread article countThe test properly validates the unread article count functionality by creating a mix of read and unread articles and asserting the correct count.
173-203: LGTM: Test for category-based article retrievalThe test properly validates category-based filtering, pagination, and sorting functionality.
| public ArticlesWithUnreadCount findAllByCategory(UUID userId, long categoryId, Pageable pageable) { | ||
|
|
||
| List<Article> articles = queryFactory | ||
| .selectFrom(article) | ||
| .join(article.category, category).fetchJoin() | ||
| .where(article.category.id.eq(categoryId)) | ||
| .offset(pageable.getOffset()) | ||
| .limit(pageable.getPageSize()) | ||
| .orderBy(article.createdAt.desc()) | ||
| .fetch(); | ||
|
|
||
| JPAQuery<Long> countQuery = queryFactory | ||
| .select(article.count()) | ||
| .from(article) | ||
| .where(article.category.id.eq(categoryId)); | ||
|
|
||
| Long unReadCount = queryFactory | ||
| .select(article.count()) | ||
| .from(article) | ||
| .where(article.category.id.eq(categoryId).and(article.isRead.isFalse())) | ||
| .fetchOne(); | ||
|
|
||
| return new ArticlesWithUnreadCount(unReadCount, | ||
| PageableExecutionUtils.getPage(articles, pageable, countQuery::fetchOne)); | ||
| } |
There was a problem hiding this comment.
Critical Security Issue: Missing user authorization in category-based queries
The findAllByCategory method has a serious security flaw - all queries filter only by categoryId but omit the userId filter. This allows users to access articles from other users if they happen to be in the same category.
Apply this fix to add user authorization to all queries:
List<Article> articles = queryFactory
.selectFrom(article)
.join(article.category, category).fetchJoin()
- .where(article.category.id.eq(categoryId))
+ .where(article.category.id.eq(categoryId).and(article.user.id.eq(userId)))
.offset(pageable.getOffset())
.limit(pageable.getPageSize())
.orderBy(article.createdAt.desc())
.fetch();
JPAQuery<Long> countQuery = queryFactory
.select(article.count())
.from(article)
- .where(article.category.id.eq(categoryId));
+ .where(article.category.id.eq(categoryId).and(article.user.id.eq(userId)));
Long unReadCount = queryFactory
.select(article.count())
.from(article)
- .where(article.category.id.eq(categoryId).and(article.isRead.isFalse()))
+ .where(article.category.id.eq(categoryId).and(article.user.id.eq(userId)).and(article.isRead.isFalse()))
.fetchOne();🤖 Prompt for AI Agents
In
src/main/java/com/pinback/pinback_server/domain/article/domain/repository/ArticleRepositoryCustomImpl.java
lines 54 to 78, the method findAllByCategory filters articles only by categoryId
without considering userId, causing a security risk by exposing other users'
articles. To fix this, add a condition to all queries to also filter by userId,
ensuring only articles belonging to the specified user and category are
retrieved. Update the where clauses to include article.user.id.eq(userId)
combined with the existing categoryId condition.
| @DisplayName("카테고리 별로 게시글을 조회할 수 있다.") | ||
| @Test | ||
| void getByCategoryWithUnreadCount() { | ||
| //given | ||
| User user = userRepository.save(user()); | ||
| Category category = categoryRepository.save(category(user)); | ||
| Category category2 = categoryRepository.save(category(user)); | ||
|
|
||
| for (int i = 0; i < 3; i++) { | ||
| articleRepository.save(article(user, "article" + i, category)); | ||
| } | ||
|
|
||
| for (int i = 0; i < 5; i++) { | ||
| articleRepository.save(article(user, "article2" + i, category2)); | ||
| } | ||
|
|
||
| for (int i = 0; i < 5; i++) { | ||
| articleRepository.save(readArticle(user, "article3" + i, category)); | ||
| } | ||
|
|
||
| //when | ||
|
|
||
| ArticleAllResponse responses = articleManagementUsecase.getAllArticlesByCategory(user, category.getId(), 0, 5); | ||
|
|
||
| //then | ||
|
|
||
| assertThat(responses.totalUnreadArticle()).isEqualTo(3); | ||
| assertThat(responses.totalArticle()) | ||
| .isEqualTo(8); | ||
| assertThat(responses.articles().get(0).createdAt()).isNotNull(); | ||
|
|
||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Add security test for cross-user access prevention
The current tests don't verify that users cannot access articles from other users in the same category. This is critical given the security issue identified in the repository layer.
Add a test case like this to ensure proper user authorization:
@DisplayName("사용자는 다른 사용자의 아티클을 카테고리로 조회할 수 없다.")
@Test
void getCategoryArticlesWithDifferentUser() {
//given
User user1 = userRepository.save(user());
User user2 = userRepository.save(userWithEmail("user2@test.com"));
Category category = categoryRepository.save(category(user1));
// User1 creates articles in the category
for (int i = 0; i < 3; i++) {
articleRepository.save(article(user1, "user1-article" + i, category));
}
// User2 should not see User1's articles
ArticleAllResponse response = articleManagementUsecase.getAllArticlesByCategory(user2, category.getId(), 0, 10);
//then
assertThat(response.articles()).isEmpty();
assertThat(response.totalArticle()).isEqualTo(0);
}🤖 Prompt for AI Agents
In
src/test/java/com/pinback/pinback_server/domain/article/application/ArticleManagementUsecaseTest.java
around lines 205 to 236, add a new test method to verify that a user cannot
access articles belonging to another user within the same category. Create two
users, have one user create articles in a category, then attempt to retrieve
those articles using the second user. Assert that the returned article list is
empty and the total article count is zero to ensure proper authorization and
prevent cross-user data access.
🚀 PR 요약
#24 아티클을 카테고리를 입력받아 조회하는 기능을 구현합니다.
✨ PR 상세 내용
🚨 주의 사항
✅ 체크 리스트
close #24
Summary by CodeRabbit
New Features
Tests