mirror of
https://github.com/anyproto/anytype-heart.git
synced 2025-06-08 05:47:07 +09:00
GO-4459: Refactor pagination middleware
This commit is contained in:
parent
4b21551fdd
commit
2cb03241f6
2 changed files with 69 additions and 28 deletions
|
@ -1,13 +1,52 @@
|
|||
package pagination
|
||||
|
||||
import "github.com/gin-gonic/gin"
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
type Service[T any] interface {
|
||||
RespondWithPagination(c *gin.Context, statusCode int, data []T, total int, offset int, limit int, hasMore bool)
|
||||
Paginate(records []T, offset int, limit int) ([]T, bool)
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// Config holds pagination configuration options.
|
||||
type Config struct {
|
||||
DefaultPage int
|
||||
DefaultPageSize int
|
||||
MinPageSize int
|
||||
MaxPageSize int
|
||||
}
|
||||
|
||||
// RespondWithPagination returns a json response with the paginated data and corresponding metadata
|
||||
// New creates a Gin middleware for pagination with the provided Config.
|
||||
func New(cfg Config) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
page := getIntQueryParam(c, "offset", cfg.DefaultPage)
|
||||
size := getIntQueryParam(c, "limit", cfg.DefaultPageSize)
|
||||
|
||||
if size < cfg.MinPageSize || size > cfg.MaxPageSize {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{
|
||||
"error": fmt.Sprintf("limit must be between %d and %d", cfg.MinPageSize, cfg.MaxPageSize),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.Set("offset", page)
|
||||
c.Set("limit", size)
|
||||
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
// getIntQueryParam retrieves an integer query parameter or falls back to a default value.
|
||||
func getIntQueryParam(c *gin.Context, key string, defaultValue int) int {
|
||||
valStr := c.DefaultQuery(key, strconv.Itoa(defaultValue))
|
||||
val, err := strconv.Atoi(valStr)
|
||||
if err != nil || val < 0 {
|
||||
return defaultValue
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
// RespondWithPagination sends a paginated JSON response.
|
||||
func RespondWithPagination[T any](c *gin.Context, statusCode int, data []T, total int, offset int, limit int, hasMore bool) {
|
||||
c.JSON(statusCode, PaginatedResponse[T]{
|
||||
Data: data,
|
||||
|
@ -20,20 +59,23 @@ func RespondWithPagination[T any](c *gin.Context, statusCode int, data []T, tota
|
|||
})
|
||||
}
|
||||
|
||||
// Paginate paginates the given records based on the offset and limit
|
||||
// Paginate slices the records based on the offset and limit, and determines if more records are available.
|
||||
func Paginate[T any](records []T, offset int, limit int) ([]T, bool) {
|
||||
total := len(records)
|
||||
start := offset
|
||||
end := offset + limit
|
||||
|
||||
if start > total {
|
||||
start = total
|
||||
if offset < 0 || limit < 1 {
|
||||
return []T{}, len(records) > 0
|
||||
}
|
||||
|
||||
total := len(records)
|
||||
if offset > total {
|
||||
offset = total
|
||||
}
|
||||
|
||||
end := offset + limit
|
||||
if end > total {
|
||||
end = total
|
||||
}
|
||||
|
||||
paginated := records[start:end]
|
||||
paginated := records[offset:end]
|
||||
hasMore := end < total
|
||||
|
||||
return paginated, hasMore
|
||||
|
|
|
@ -4,11 +4,11 @@ import (
|
|||
"github.com/gin-gonic/gin"
|
||||
swaggerFiles "github.com/swaggo/files"
|
||||
ginSwagger "github.com/swaggo/gin-swagger"
|
||||
"github.com/webstradev/gin-pagination/v2/pkg/pagination"
|
||||
|
||||
"github.com/anyproto/anytype-heart/cmd/api/auth"
|
||||
"github.com/anyproto/anytype-heart/cmd/api/export"
|
||||
"github.com/anyproto/anytype-heart/cmd/api/object"
|
||||
"github.com/anyproto/anytype-heart/cmd/api/pagination"
|
||||
"github.com/anyproto/anytype-heart/cmd/api/search"
|
||||
"github.com/anyproto/anytype-heart/cmd/api/space"
|
||||
)
|
||||
|
@ -18,20 +18,19 @@ func (s *Server) NewRouter() *gin.Engine {
|
|||
router := gin.Default()
|
||||
|
||||
// Pagination middleware setup
|
||||
paginator := pagination.New(
|
||||
pagination.WithPageText("offset"),
|
||||
pagination.WithSizeText("limit"),
|
||||
pagination.WithDefaultPage(0),
|
||||
pagination.WithDefaultPageSize(100),
|
||||
pagination.WithMinPageSize(1),
|
||||
pagination.WithMaxPageSize(1000),
|
||||
)
|
||||
paginator := pagination.New(pagination.Config{
|
||||
DefaultPage: 0,
|
||||
DefaultPageSize: 100,
|
||||
MinPageSize: 1,
|
||||
MaxPageSize: 1000,
|
||||
})
|
||||
|
||||
// Swagger route
|
||||
router.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
|
||||
|
||||
// API routes
|
||||
v1 := router.Group("/v1")
|
||||
v1.Use(paginator)
|
||||
v1.Use(s.initAccountInfo())
|
||||
v1.Use(s.ensureAuthenticated())
|
||||
{
|
||||
|
@ -44,20 +43,20 @@ func (s *Server) NewRouter() *gin.Engine {
|
|||
v1.POST("/spaces/:space_id/objects/export/:format", export.GetSpaceExportHandler(s.exportService))
|
||||
|
||||
// Object
|
||||
v1.GET("/spaces/:space_id/objects", paginator, object.GetObjectsHandler(s.objectService))
|
||||
v1.GET("/spaces/:space_id/objects", object.GetObjectsHandler(s.objectService))
|
||||
v1.GET("/spaces/:space_id/objects/:object_id", object.GetObjectHandler(s.objectService))
|
||||
v1.DELETE("/spaces/:space_id/objects/:object_id", object.DeleteObjectHandler(s.objectService))
|
||||
v1.GET("/spaces/:space_id/object_types", paginator, object.GetTypesHandler(s.objectService))
|
||||
v1.GET("/spaces/:space_id/object_types/:typeId/templates", paginator, object.GetTemplatesHandler(s.objectService))
|
||||
v1.GET("/spaces/:space_id/object_types", object.GetTypesHandler(s.objectService))
|
||||
v1.GET("/spaces/:space_id/object_types/:typeId/templates", object.GetTemplatesHandler(s.objectService))
|
||||
v1.POST("/spaces/:space_id/objects", object.CreateObjectHandler(s.objectService))
|
||||
v1.PUT("/spaces/:space_id/objects/:object_id", object.UpdateObjectHandler(s.objectService))
|
||||
|
||||
// Search
|
||||
v1.GET("/search", paginator, search.SearchHandler(s.searchService))
|
||||
v1.GET("/search", search.SearchHandler(s.searchService))
|
||||
|
||||
// Space
|
||||
v1.GET("/spaces", paginator, space.GetSpacesHandler(s.spaceService))
|
||||
v1.GET("/spaces/:space_id/members", paginator, space.GetMembersHandler(s.spaceService))
|
||||
v1.GET("/spaces", space.GetSpacesHandler(s.spaceService))
|
||||
v1.GET("/spaces/:space_id/members", space.GetMembersHandler(s.spaceService))
|
||||
v1.POST("/spaces", space.CreateSpaceHandler(s.spaceService))
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue