1
0
Fork 0
mirror of https://github.com/anyproto/anytype-heart.git synced 2025-06-09 17:44:59 +09:00

GO-4459 Refactor pagination and 'total' calculation

This commit is contained in:
Jannis Metrikat 2024-12-29 17:52:17 +01:00
parent ffb4c8b87f
commit 5001594a8f
No known key found for this signature in database
GPG key ID: B223CAC5AAF85615
7 changed files with 62 additions and 81 deletions

View file

@ -1364,9 +1364,9 @@ const docTemplate = `{
"example": 0
},
"total": {
"description": "the total number of items returned",
"description": "the total number of items available on that endpoint",
"type": "integer",
"example": 100
"example": 1024
}
}
},

View file

@ -1358,9 +1358,9 @@
"example": 0
},
"total": {
"description": "the total number of items returned",
"description": "the total number of items available on that endpoint",
"type": "integer",
"example": 100
"example": 1024
}
}
},

View file

@ -266,8 +266,8 @@ definitions:
example: 0
type: integer
total:
description: the total number of items returned
example: 100
description: the total number of items available on that endpoint
example: 1024
type: integer
type: object
api.Reactions:

View file

@ -129,9 +129,7 @@ func (a *ApiServer) getSpacesHandler(c *gin.Context) {
Type: model.BlockContentDataviewSort_Asc,
},
},
Keys: []string{"targetSpaceId", "name", "iconEmoji", "iconImage"},
Offset: int32(offset),
Limit: int32(limit + 1),
Keys: []string{"targetSpaceId", "name", "iconEmoji", "iconImage"},
})
if resp.Error.Code != pb.RpcObjectSearchResponseError_NULL {
@ -144,8 +142,10 @@ func (a *ApiServer) getSpacesHandler(c *gin.Context) {
return
}
spaces := make([]Space, 0, len(resp.Records))
for _, record := range resp.Records {
paginatedSpaces, hasMore := paginate(resp.Records, offset, limit)
spaces := make([]Space, 0, len(paginatedSpaces))
for _, record := range paginatedSpaces {
workspace, statusCode, errorMessage := a.getWorkspaceInfo(record.Fields["targetSpaceId"].GetStringValue())
if statusCode != http.StatusOK {
c.JSON(statusCode, gin.H{"message": errorMessage})
@ -158,13 +158,7 @@ func (a *ApiServer) getSpacesHandler(c *gin.Context) {
spaces = append(spaces, workspace)
}
hasNext := false
if len(spaces) > limit {
hasNext = true
spaces = spaces[:limit]
}
respondWithPagination(c, http.StatusOK, spaces, len(spaces), offset, limit, hasNext)
respondWithPagination(c, http.StatusOK, spaces, len(resp.Records), offset, limit, hasMore)
}
// createSpaceHandler creates a new space
@ -254,9 +248,7 @@ func (a *ApiServer) getMembersHandler(c *gin.Context) {
Type: model.BlockContentDataviewSort_Asc,
},
},
Keys: []string{"id", "name", "iconEmoji", "iconImage", "identity", "globalName", "participantPermissions"},
Offset: int32(offset),
Limit: int32(limit + 1),
Keys: []string{"id", "name", "iconEmoji", "iconImage", "identity", "globalName", "participantPermissions"},
})
if resp.Error.Code != pb.RpcObjectSearchResponseError_NULL {
@ -269,8 +261,10 @@ func (a *ApiServer) getMembersHandler(c *gin.Context) {
return
}
members := make([]Member, 0, len(resp.Records))
for _, record := range resp.Records {
paginatedMembers, hasMore := paginate(resp.Records, offset, limit)
members := make([]Member, 0, len(paginatedMembers))
for _, record := range paginatedMembers {
icon := a.getIconFromEmojiOrImage(record.Fields["iconEmoji"].GetStringValue(), record.Fields["iconImage"].GetStringValue())
member := Member{
@ -286,13 +280,7 @@ func (a *ApiServer) getMembersHandler(c *gin.Context) {
members = append(members, member)
}
hasNext := false
if len(members) > limit {
hasNext = true
members = members[:limit]
}
respondWithPagination(c, http.StatusOK, members, len(members), offset, limit, hasNext)
respondWithPagination(c, http.StatusOK, members, len(resp.Records), offset, limit, hasMore)
}
// getObjectsHandler retrieves objects in a specific space
@ -339,9 +327,7 @@ func (a *ApiServer) getObjectsHandler(c *gin.Context) {
IncludeTime: true,
EmptyPlacement: model.BlockContentDataviewSort_NotSpecified,
}},
Keys: []string{"id", "name", "type", "layout", "iconEmoji", "iconImage"},
Offset: int32(offset),
Limit: int32(limit + 1),
Keys: []string{"id", "name", "type", "layout", "iconEmoji", "iconImage"},
})
if resp.Error.Code != pb.RpcObjectSearchResponseError_NULL {
@ -354,8 +340,10 @@ func (a *ApiServer) getObjectsHandler(c *gin.Context) {
return
}
objects := make([]Object, 0, len(resp.Records))
for _, record := range resp.Records {
paginatedObjects, hasMore := paginate(resp.Records, offset, limit)
objects := make([]Object, 0, len(paginatedObjects))
for _, record := range paginatedObjects {
icon := a.getIconFromEmojiOrImage(record.Fields["iconEmoji"].GetStringValue(), record.Fields["iconImage"].GetStringValue())
objectTypeName, statusCode, errorMessage := a.resolveTypeToName(spaceId, record.Fields["type"].GetStringValue())
if statusCode != http.StatusOK {
@ -384,13 +372,7 @@ func (a *ApiServer) getObjectsHandler(c *gin.Context) {
objects = append(objects, object)
}
hasNext := false
if len(objects) > limit {
hasNext = true
objects = objects[:limit]
}
respondWithPagination(c, http.StatusOK, objects, len(objects), offset, limit, hasNext)
respondWithPagination(c, http.StatusOK, objects, len(resp.Records), offset, limit, hasMore)
}
// getObjectHandler retrieves a specific object in a space
@ -559,9 +541,7 @@ func (a *ApiServer) getObjectTypesHandler(c *gin.Context) {
Type: model.BlockContentDataviewSort_Asc,
},
},
Keys: []string{"id", "uniqueKey", "name", "iconEmoji"},
Offset: int32(offset),
Limit: int32(limit + 1),
Keys: []string{"id", "uniqueKey", "name", "iconEmoji"},
})
if resp.Error.Code != pb.RpcObjectSearchResponseError_NULL {
@ -574,8 +554,10 @@ func (a *ApiServer) getObjectTypesHandler(c *gin.Context) {
return
}
objectTypes := make([]ObjectType, 0, len(resp.Records))
for _, record := range resp.Records {
paginatedTypes, hasMore := paginate(resp.Records, offset, limit)
objectTypes := make([]ObjectType, 0, len(paginatedTypes))
for _, record := range paginatedTypes {
objectTypes = append(objectTypes, ObjectType{
Type: "object_type",
Id: record.Fields["id"].GetStringValue(),
@ -585,13 +567,7 @@ func (a *ApiServer) getObjectTypesHandler(c *gin.Context) {
})
}
hasNext := false
if len(objectTypes) > limit {
hasNext = true
objectTypes = objectTypes[:limit]
}
respondWithPagination(c, http.StatusOK, objectTypes, len(objectTypes), offset, limit, hasNext)
respondWithPagination(c, http.StatusOK, objectTypes, len(resp.Records), offset, limit, hasMore)
}
// getObjectTypeTemplatesHandler retrieves a list of templates for a specific object type in a space
@ -650,9 +626,7 @@ func (a *ApiServer) getObjectTypeTemplatesHandler(c *gin.Context) {
Value: pbtypes.String(templateTypeId),
},
},
Keys: []string{"id", "targetObjectType", "name", "iconEmoji"},
Offset: int32(offset),
Limit: int32(limit + 1),
Keys: []string{"id", "targetObjectType", "name", "iconEmoji"},
})
if templateObjectsResp.Error.Code != pb.RpcObjectSearchResponseError_NULL {
@ -673,8 +647,10 @@ func (a *ApiServer) getObjectTypeTemplatesHandler(c *gin.Context) {
}
// Finally, open each template and populate the response
templates := make([]ObjectTemplate, 0, len(templateIds))
for _, templateId := range templateIds {
paginatedTemplates, hasMore := paginate(templateIds, offset, limit)
templates := make([]ObjectTemplate, 0, len(paginatedTemplates))
for _, templateId := range paginatedTemplates {
templateResp := a.mw.ObjectShow(c.Request.Context(), &pb.RpcObjectShowRequest{
SpaceId: spaceId,
ObjectId: templateId,
@ -693,13 +669,7 @@ func (a *ApiServer) getObjectTypeTemplatesHandler(c *gin.Context) {
})
}
hasNext := false
if len(templates) > limit {
hasNext = true
templates = templates[:limit]
}
respondWithPagination(c, http.StatusOK, templates, len(templates), offset, limit, hasNext)
respondWithPagination(c, http.StatusOK, templates, len(templateIds), offset, limit, hasMore)
}
// searchHandler searches and retrieves objects across all the spaces
@ -808,9 +778,7 @@ func (a *ApiServer) searchHandler(c *gin.Context) {
IncludeTime: true,
EmptyPlacement: model.BlockContentDataviewSort_NotSpecified,
}},
Keys: []string{"id", "name", "type", "layout", "iconEmoji", "iconImage"},
Offset: int32(offset),
Limit: int32(limit + 1),
Keys: []string{"id", "name", "type", "layout", "iconEmoji", "iconImage"},
// TODO split limit between spaces
// Limit: paginationLimitPerSpace,
// FullText: searchTerm,
@ -858,13 +826,9 @@ func (a *ApiServer) searchHandler(c *gin.Context) {
})
// TODO: solve global pagination vs per space pagination
hasNext := false
if len(searchResults) > limit {
hasNext = true
searchResults = searchResults[:limit]
}
paginatedResults, hasMore := paginate(searchResults, offset, limit)
respondWithPagination(c, http.StatusOK, searchResults, len(searchResults), offset, limit, hasNext)
respondWithPagination(c, http.StatusOK, paginatedResults, len(searchResults), offset, limit, hasMore)
}
// getChatMessagesHandler retrieves last chat messages

View file

@ -232,9 +232,7 @@ func TestApiServer_GetSpacesHandler(t *testing.T) {
Type: model.BlockContentDataviewSort_Asc,
},
},
Keys: []string{"targetSpaceId", "name", "iconEmoji", "iconImage"},
Offset: offset,
Limit: limit + 1,
Keys: []string{"targetSpaceId", "name", "iconEmoji", "iconImage"},
}).Return(&pb.RpcObjectSearchResponse{
Records: []*types.Struct{
{

View file

@ -242,14 +242,33 @@ func (a *ApiServer) getTags(resp *pb.RpcObjectShowResponse) []Tag {
return tags
}
func respondWithPagination[T any](c *gin.Context, statusCode int, data []T, total, offset, limit int, hasNext bool) {
// respondWithPagination returns a json response with the paginated data and corresponding metadata
func respondWithPagination[T any](c *gin.Context, statusCode int, data []T, total, offset, limit int, hasMore bool) {
c.JSON(statusCode, PaginatedResponse[T]{
Data: data,
Pagination: PaginationMeta{
Total: total,
Offset: offset,
Limit: limit,
HasMore: hasNext,
HasMore: hasMore,
},
})
}
// paginate paginates the given records based on the offset and limit
func paginate[T any](records []T, offset, limit int) ([]T, bool) {
total := len(records)
start := offset
end := offset + limit
if start > total {
start = total
}
if end > total {
end = total
}
paginated := records[start:end]
hasMore := end < total
return paginated, hasMore
}

View file

@ -1,7 +1,7 @@
package api
type PaginationMeta struct {
Total int `json:"total" example:"100"` // the total number of items returned
Total int `json:"total" example:"1024"` // the total number of items available on that endpoint
Offset int `json:"offset" example:"0"` // the current offset
Limit int `json:"limit" example:"100"` // the current limit
HasMore bool `json:"has_more" example:"true"` // whether there are more items available