-
-
Notifications
You must be signed in to change notification settings - Fork 0
[feature] Add categories endpoint #64
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
Conversation
|
""" WalkthroughThis change introduces a new categories API endpoint and refactors post-related handler logic for improved modularity. It adds repository, handler, and payload code for categories, centralizes pagination and payload transformation utilities, and removes redundant post handler files. The router and application boot sequence are updated to register the new categories route. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Poem
Note ⚡️ Unit Test Generation is now available in beta!Learn more here, or try it out under "Finishing Touches" below. ✨ Finishing Touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Summary of Changes
Hello @gocanto, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!
This pull request introduces a new API endpoint for categories, enabling clients to fetch paginated category data. Alongside this new feature, I've undertaken a significant refactoring effort to centralize and standardize pagination handling across the application's API handlers and to streamline the data structures used for API responses, ensuring a cleaner and more consistent public interface.
Highlights
- New API Endpoint: I've added a new
GET /categoriesendpoint to the application. This endpoint allows clients to retrieve a paginated list of categories, including their associated posts. - Pagination Logic Refactoring: I've extracted the common pagination parsing logic into a new, reusable
handler/paginatepackage. This new utility is now used by both the new categories endpoint and the existing posts endpoint, promoting consistency and reducing duplication. - API Response Structure Refinement: I've refined the API response structures for
User,Category, andTagdata within thehandler/postspackage. Specifically, I've renamed*Datastructs to*Responseand removed internal database timestamps (CreatedAt,UpdatedAt) from these public-facing response objects for a cleaner API contract. - Repository Method Renaming: For consistency, I've renamed the
GetPostsmethod in thedatabase/repository/posts.gotoGetAll, aligning it with the newly introducedGetAllmethod in thecategoriesrepository.
Using Gemini Code Assist
The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.
Invoking Gemini
You can request assistance from Gemini at any point in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands.
| Feature | Command | Description |
|---|---|---|
| Code Review | /gemini review |
Performs a code review for the current pull request in its current state. |
| Pull Request Summary | /gemini summary |
Provides a summary of the current pull request in its current state. |
| Comment | @gemini-code-assist | Responds in comments when explicitly tagged, both in issue comments and review comments. |
| Help | /gemini help |
Displays a list of available commands. |
Customization
To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.
Limitations & Feedback
Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments or fill out our survey to provide feedback.
You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.
Footnotes
-
Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution. ↩
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code Review
This pull request introduces a new /categories endpoint with pagination. The review identifies a critical bug in the database query for fetching categories which would lead to incorrect pagination results. A code suggestion is provided to fix this. Additionally, a medium-severity suggestion is made to improve the project structure by relocating the now-shared response DTOs to a more appropriate package to enhance maintainability.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
♻️ Duplicate comments (2)
database/repository/categories.go (2)
20-28: Critical: LIMIT clause may affect count calculation.Based on past review feedback, applying
LIMITto the base query before counting (line 23, then count on line 26) can cause incorrect pagination metadata. The count operation may be limited to the page size rather than counting all matching records.Verify that
pagination.Countproperly isolates the counting query from the LIMIT clause:#!/bin/bash # Description: Check if pagination.Count properly handles LIMIT isolation # Expected: Count function should use a separate query session that ignores LIMIT ast-grep --pattern 'func Count[$_]($_) { $$$ }'
35-35: Remove unnecessary Distinct() call.As noted in past reviews, the
Distinct()call is not needed since this query doesn't produce duplicate rows.err := query.Preload("Posts"). Limit(paginate.Limit). Offset(offset). - Distinct(). Find(&categories).Error
🧹 Nitpick comments (4)
database/repository/pagination/support.go (2)
5-5: Simplify the generic type constraint.The generic type parameter
T *int64is unnecessary complexity. Since the function only works with*int64, use the concrete type directly.-func Count[T *int64](numItems T, query *gorm.DB, session *gorm.Session, distinct string) error { +func Count(numItems *int64, query *gorm.DB, session *gorm.Session, distinct string) error {
5-15: Add function documentation.The function lacks documentation comments explaining its purpose, parameters, and return values. This is especially important for utility functions that will be used across multiple repositories.
+// Count executes a distinct count query on the provided GORM query and stores the result in numItems. +// It clones the base query using the provided session and applies a distinct filter on the specified column. +// Returns an error if the count operation fails, otherwise returns nil. func Count(numItems *int64, query *gorm.DB, session *gorm.Session, distinct string) error {handler/payload/categories.go (1)
12-25: Optimize slice allocation for better performance.The function uses
appendin a loop without pre-allocating the slice capacity. For better performance, especially with large datasets, consider pre-allocating the slice with the known length.func GetCategoriesResponse(categories []database.Category) []CategoryResponse { - var data []CategoryResponse + data := make([]CategoryResponse, 0, len(categories)) for _, category := range categories { data = append(data, CategoryResponse{ UUID: category.UUID, Name: category.Name, Slug: category.Slug, Description: category.Description, }) } return data }handler/payload/tags.go (1)
12-25: Optimize slice allocation for better performance.The current implementation uses
appendin a loop which can cause multiple slice reallocations. Pre-allocate the slice with known capacity for better performance.func GetTagsResponse(tags []database.Tag) []TagResponse { - var data []TagResponse + data := make([]TagResponse, 0, len(tags)) for _, tag := range tags { data = append(data, TagResponse{ UUID: tag.UUID, Name: tag.Name, Slug: tag.Slug, Description: tag.Description, }) } return data }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (14)
boost/app.go(1 hunks)boost/router.go(1 hunks)database/repository/categories.go(2 hunks)database/repository/pagination/support.go(1 hunks)database/repository/posts.go(2 hunks)handler/categories.go(1 hunks)handler/paginate/paginate.go(1 hunks)handler/payload/categories.go(1 hunks)handler/payload/posts.go(1 hunks)handler/payload/tags.go(1 hunks)handler/payload/users.go(1 hunks)handler/posts.go(5 hunks)handler/posts/response.go(0 hunks)handler/posts/transformer.go(0 hunks)
💤 Files with no reviewable changes (2)
- handler/posts/response.go
- handler/posts/transformer.go
🔇 Additional comments (20)
boost/router.go (1)
45-52: LGTM!The
Categories()method follows the established pattern used by other route handlers in this file. The implementation correctly creates the repository, handler, applies the middleware pipeline, and registers the route.boost/app.go (1)
74-74: LGTM!The addition of
router.Categories()correctly integrates the new categories route into the application boot sequence. The placement afterrouter.Posts()is logical and follows the established pattern.handler/payload/categories.go (1)
5-10: LGTM!The
CategoryResponsestruct is well-defined with appropriate JSON tags. The fields correctly map to the correspondingdatabase.Categoryfields as shown in the relevant code snippet fromapp/database/schema.go.handler/payload/users.go (1)
3-13: LGTM!The
UserResponsestruct is well-defined with appropriate field types and JSON tags following snake_case naming conventions. The structure comprehensively covers user profile information needed for API responses.handler/payload/tags.go (1)
5-10: LGTM! TagResponse struct correctly maps database fields.The struct properly defines JSON serialization tags and maps the key fields from the database.Tag model.
handler/paginate/paginate.go (1)
9-37: LGTM! Solid pagination parameter parsing with proper validation.The function correctly:
- Handles missing or invalid query parameters gracefully
- Applies sensible defaults using pagination constants
- Validates bounds to prevent invalid page/limit values
- Uses defensive programming practices
This is a good centralization of pagination logic that was previously scattered.
database/repository/posts.go (2)
18-18: Good method rename for consistency.Renaming
GetPoststoGetAllaligns with the newCategories.GetAllmethod, providing a consistent interface across repositories.
29-29: LGTM! Using centralized counting helper.The switch to
pagination.Counthelper function centralizes the distinct counting logic and should provide consistent behavior across repositories.handler/posts.go (2)
8-9: LGTM! Clean import organization for refactored packages.The new imports properly separate concerns with dedicated
paginateandpayloadpackages.
29-29: Excellent refactoring to use modularized payload and pagination utilities.The changes consistently update all references to use the new:
payloadpackage for request/response transformationspaginatepackage for pagination parameter parsing- Repository method rename from
GetPoststoGetAllThis improves code organization and reusability across handlers.
Also applies to: 37-39, 50-50, 63-63, 74-74
handler/categories.go (5)
3-13: LGTM! Clean import structure and dependency organization.The imports are well-organized with proper aliasing for
baseHttpto avoid naming conflicts with the custom HTTP package. The dependencies follow the established project structure.
15-23: LGTM! Proper dependency injection pattern.The
CategoriesHandlerstruct and constructor follow the established dependency injection pattern used throughout the codebase. The constructor function naming convention withMakeprefix is consistent.
25-33: LGTM! Proper error handling and logging.The method signature follows the established pattern with
*http.ApiErrorreturn type. Error handling includes structured logging and appropriate HTTP error response.
35-45: LGTM! Consistent data transformation pattern.The use of
pagination.HydratePaginationwith a transformation function follows the established pattern. The inline transformation function correctly maps database fields to response payload fields, matching theCategoryResponsestructure.
47-54: LGTM! Proper JSON encoding with error handling.The JSON encoding includes proper error handling with structured logging and appropriate error response. The method returns
nilon success, following the established pattern.handler/payload/posts.go (5)
1-10: LGTM! Clean package structure and imports.The package declaration and imports are well-organized. The use of
baseHttpalias prevents naming conflicts with the custom HTTP utilities.
12-18: LGTM! Comprehensive filter structure.The
IndexRequestBodystruct provides comprehensive filtering options for posts including title, author, category, tag, and text search. The JSON tags follow proper naming conventions.
20-35: LGTM! Complete post response structure.The
PostResponsestruct correctly maps all fields from the databasePostentity as confirmed by the schema inapp/database/schema.go. The field mappings are accurate:
- Basic fields (UUID, slug, title, etc.) match the database structure
- Timestamp fields are properly typed
- Associated entities (Categories, Tags, Author) are correctly represented
- JSON tags follow consistent naming conventions
46-50: LGTM! Proper slug extraction and normalization.The
GetSlugFromfunction correctly extracts the slug from the URL path parameter and applies proper normalization (trimming whitespace and converting to lowercase).
52-77: LGTM! Comprehensive post response transformation.The
GetPostsResponsefunction correctly transforms databasePostentities to API responses:
- All basic fields are properly mapped
- Associated categories and tags are transformed using dedicated functions
- Author information is correctly embedded as
UserResponse- Field mappings match the database schema structure from
app/database/schema.goThe transformation is comprehensive and maintains data integrity.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
database/repository/categories.go(2 hunks)database/repository/pagination/pagination.go(1 hunks)handler/categories.go(1 hunks)handler/paginate/paginate.go(1 hunks)handler/posts.go(5 hunks)
✅ Files skipped from review due to trivial changes (2)
- database/repository/pagination/pagination.go
- handler/categories.go
🚧 Files skipped from review as they are similar to previous changes (2)
- handler/paginate/paginate.go
- handler/posts.go
🔇 Additional comments (3)
database/repository/categories.go (3)
7-7: LGTM!The pagination package import is necessary for the new GetAll method functionality.
30-32: LGTM! Pagination counting logic is now correct.The use of
pagination.Countwith the group parameter properly counts distinct categories before applying LIMIT, addressing the previous review concern about incorrect pagination metadata.
48-51: LGTM!The pagination result construction is correct. Setting the number of items and using
MakePaginationfollows the expected pattern for paginated responses.
|
@coderabbitai full review |
✅ Actions performedFull review triggered. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (1)
handler/payload/posts.go (1)
37-45: LGTM! Past review feedback addressed.The
GetPostsFiltersFromfunction now correctly maps all fields including theTextfield that was previously missing. All request filters are properly transformed to query filters.
🧹 Nitpick comments (2)
handler/payload/categories.go (1)
12-25: Consider pre-allocating slice capacity for better performance.The transformation logic is correct, but you can improve performance by pre-allocating the slice capacity to avoid multiple reallocations during append operations.
func GetCategoriesResponse(categories []database.Category) []CategoryResponse { - var data []CategoryResponse + data := make([]CategoryResponse, 0, len(categories)) for _, category := range categories { data = append(data, CategoryResponse{ UUID: category.UUID, Name: category.Name, Slug: category.Slug, Description: category.Description, }) } return data }handler/payload/tags.go (1)
12-25: Consider pre-allocating slice capacity for better performance.The transformation logic is correct, but the slice could be pre-allocated with known capacity to avoid potential reallocations during append operations.
-func GetTagsResponse(tags []database.Tag) []TagResponse { - var data []TagResponse +func GetTagsResponse(tags []database.Tag) []TagResponse { + data := make([]TagResponse, 0, len(tags))
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (15)
boost/app.go(1 hunks)boost/router.go(1 hunks)database/repository/categories.go(2 hunks)database/repository/pagination/pagination.go(1 hunks)database/repository/pagination/support.go(1 hunks)database/repository/posts.go(2 hunks)handler/categories.go(1 hunks)handler/paginate/paginate.go(1 hunks)handler/payload/categories.go(1 hunks)handler/payload/posts.go(1 hunks)handler/payload/tags.go(1 hunks)handler/payload/users.go(1 hunks)handler/posts.go(5 hunks)handler/posts/response.go(0 hunks)handler/posts/transformer.go(0 hunks)
💤 Files with no reviewable changes (2)
- handler/posts/response.go
- handler/posts/transformer.go
🔇 Additional comments (20)
database/repository/pagination/support.go (1)
5-15: LGTM! Well-designed generic pagination helper.The
Countfunction provides a clean, reusable solution for counting distinct records with proper query cloning and error handling. The generic constraintT *int64ensures type safety while maintaining flexibility.database/repository/pagination/pagination.go (1)
6-7: Good refactoring to support entity-specific pagination limits.Separating the pagination limits allows for more granular control over API performance. The smaller limit for categories (5) compared to posts (10) makes sense given different usage patterns.
boost/app.go (1)
74-74: LGTM! Proper integration of categories route.The addition of
router.Categories()follows the established pattern and correctly integrates the new endpoint into the application boot sequence.boost/router.go (2)
45-52: LGTM! Categories endpoint follows established patterns.The new
Categories()method correctly follows the established routing pattern with proper repository initialization, handler creation, and middleware wrapping.
41-41: Confirm breaking change for/postsendpointThe update to
r.Mux.HandleFunc("POST /posts", index)replaces the previous GET listing route and is a breaking change that deviates from REST conventions. I did not find any references to the old GET
/postsroute in API docs (MD/TXT/JSON) within this repo.Please verify:
- All existing clients, SDKs, and API documentation have been updated to use POST
/posts.- Consider deprecating the old GET
/postsendpoint (or providing both) and clearly communicating the change to consumers.The new
GET /categoriesroute follows the established pattern and looks good.handler/payload/users.go (1)
3-13: LGTM! Clean and well-structured user response payload.The
UserResponsestruct follows Go naming conventions, uses consistent snake_case JSON tags, and includes appropriate fields for user profile data. The structure is clean and ready for API responses.handler/payload/tags.go (1)
5-10: LGTM! Well-structured tag response payload.The
TagResponsestruct follows consistent naming conventions and includes appropriate fields for tag representation.database/repository/posts.go (2)
29-31: No changes needed for pagination.Count generic parameter
TheCountfunction is declared asfunc Count[T *int64](…), so specializing it asCount[*int64](&numItems, …)is exactly correct.
18-18: Generic type parameter usage and method rename are correct
- The
GetPosts→GetAllrename is applied consistently; no remaining calls toGetPostswere found.- The call
pagination.Count[*int64](&numItems, …)matches the definition
func Count[T *int64](numItems T, …) error—&numItemsis indeed a*int64, so the explicit type argument is valid.The original concern about the generic type parameter was unfounded.
Likely an incorrect or invalid review comment.
handler/posts.go (4)
8-9: LGTM! Clean import updates for the refactoring.The new imports align perfectly with the package restructuring, moving from the old posts-specific package to the more generic payload and paginate packages.
29-29: Excellent refactoring to use the new payload and pagination utilities.The migration from
posts.IndexRequestBodytopayload.IndexRequestBodyand the use ofpaginate.MakeFromwith the new filter extraction logic shows good separation of concerns.Also applies to: 38-39
37-37: Perfect alignment with repository method rename and response transformation.The updated calls to
GetAll(renamed fromGetPosts) andpayload.GetPostsResponsemaintain consistency with the broader refactoring effort.Also applies to: 50-50
63-63: Consistent use of new payload utilities in Show method.The migration to
payload.GetSlugFromandpayload.GetPostsResponsemaintains consistency with the Index method refactoring.Also applies to: 74-74
database/repository/categories.go (1)
16-52: LGTM! Past review feedback has been properly addressed.The
GetAllmethod implementation correctly addresses the previous review concerns:
✅ Pagination counting fixed: Uses
pagination.Counthelper to count distinct categories before applying LIMIT/OFFSET, ensuring accurate pagination metadata.✅ Preload filtering implemented: The
Preload("Posts", ...)now correctly filters to only published, non-deleted posts instead of loading all posts.✅ Proper grouping: Uses
GROUP BY categories.id, categories.slugto handle potential duplicates from JOINs.The query logic is sound - using INNER JOINs to only return categories with published posts appears intentional for an API endpoint that displays content.
handler/categories.go (2)
15-23: LGTM! Clean handler structure and constructor.The
CategoriesHandlerstruct andMakeCategoriesHandlerconstructor follow good Go patterns with clear dependency injection of the repository.
25-54: Well-implemented HTTP handler with proper error handling.The
Indexmethod demonstrates good practices:
- ✅ Proper pagination: Uses
paginate.MakeFromwith sensible default page size of 5- ✅ Error handling: Logs errors and returns appropriate HTTP responses
- ✅ Response transformation: Uses pagination hydration to convert domain models to API responses
- ✅ JSON encoding safety: Handles encoding errors gracefully
The inline transformation function on lines 37-44 keeps the response mapping close to usage, which is appropriate for this simple case.
handler/payload/posts.go (4)
12-18: LGTM! Complete request structure for post filtering.The
IndexRequestBodystruct includes all necessary fields for comprehensive post filtering including title, author, category, tag, and text search.
20-35: Well-structured response payload with proper associations.The
PostResponsestruct provides a comprehensive API response format with:
- All essential post metadata fields
- Proper timestamp handling with
*time.Timefor nullablePublishedAt- Clean separation of associations (categories, tags, author)
47-51: LGTM! Safe path parameter extraction.The
GetSlugFromfunction safely extracts and normalizes the slug parameter with proper trimming and case normalization.
53-78: Comprehensive post response transformation.The
GetPostsResponsefunction provides complete mapping from database entity to API response, properly handling all fields and nested associations. The inline author mapping ensures all user fields are included.
Summary by CodeRabbit
New Features
Refactor
/postsroute to accept POST requests instead of GET.Bug Fixes
Chores