mirror of https://github.com/usememos/memos.git
feat: implement activity pagination
This commit is contained in:
parent
da2dd80e2f
commit
b962f0c159
|
|
@ -15,22 +15,39 @@ import (
|
|||
)
|
||||
|
||||
func (s *APIV1Service) ListActivities(ctx context.Context, request *v1pb.ListActivitiesRequest) (*v1pb.ListActivitiesResponse, error) {
|
||||
// Set default page size if not specified
|
||||
pageSize := request.PageSize
|
||||
if pageSize <= 0 || pageSize > 1000 {
|
||||
pageSize = 100
|
||||
var limit, offset int
|
||||
if request.PageToken != "" {
|
||||
var pageToken v1pb.PageToken
|
||||
if err := unmarshalPageToken(request.PageToken, &pageToken); err != nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "invalid page token: %v", err)
|
||||
}
|
||||
limit = int(pageToken.Limit)
|
||||
offset = int(pageToken.Offset)
|
||||
} else {
|
||||
limit = int(request.PageSize)
|
||||
}
|
||||
|
||||
// TODO: Implement pagination with page_token and use pageSize for limiting
|
||||
// For now, we'll fetch all activities and the pageSize will be used in future pagination implementation
|
||||
_ = pageSize // Acknowledge pageSize variable to avoid linter warning
|
||||
|
||||
activities, err := s.Store.ListActivities(ctx, &store.FindActivity{})
|
||||
if limit <= 0 {
|
||||
limit = DefaultPageSize
|
||||
}
|
||||
limitPlusOne := limit + 1
|
||||
activities, err := s.Store.ListActivities(ctx, &store.FindActivity{
|
||||
Limit: &limitPlusOne,
|
||||
Offset: &offset,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "failed to list activities: %v", err)
|
||||
}
|
||||
|
||||
var activityMessages []*v1pb.Activity
|
||||
nextPageToken := ""
|
||||
if len(activities) == limitPlusOne {
|
||||
activities = activities[:limit]
|
||||
nextPageToken, err = getPageToken(limit, offset+limit)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "failed to get next page token, error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
for _, activity := range activities {
|
||||
activityMessage, err := s.convertActivityFromStore(ctx, activity)
|
||||
if err != nil {
|
||||
|
|
@ -40,8 +57,8 @@ func (s *APIV1Service) ListActivities(ctx context.Context, request *v1pb.ListAct
|
|||
}
|
||||
|
||||
return &v1pb.ListActivitiesResponse{
|
||||
Activities: activityMessages,
|
||||
// TODO: Implement next_page_token for pagination
|
||||
Activities: activityMessages,
|
||||
NextPageToken: nextPageToken,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,67 @@
|
|||
package test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
apiv1 "github.com/usememos/memos/proto/gen/api/v1"
|
||||
)
|
||||
|
||||
func TestListActivities(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
ts := NewTestService(t)
|
||||
defer ts.Cleanup()
|
||||
|
||||
// Create userOne
|
||||
userOne, err := ts.CreateRegularUser(ctx, "test-user-1")
|
||||
require.NoError(t, err)
|
||||
userOneCtx := ts.CreateUserContext(ctx, userOne.ID)
|
||||
|
||||
// Create userTwo
|
||||
userTwo, err := ts.CreateRegularUser(ctx, "test-user-2")
|
||||
require.NoError(t, err)
|
||||
userTwoCtx := ts.CreateUserContext(ctx, userTwo.ID)
|
||||
|
||||
// UserOne creates a memo
|
||||
memo, err := ts.Service.CreateMemo(userOneCtx, &apiv1.CreateMemoRequest{
|
||||
Memo: &apiv1.Memo{
|
||||
Content: "Base memo",
|
||||
Visibility: apiv1.Visibility_PUBLIC,
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// UserTwo creates 15 comments on the memo to generate 15 activities
|
||||
for i := 0; i < 15; i++ {
|
||||
_, err := ts.Service.CreateMemoComment(userTwoCtx, &apiv1.CreateMemoCommentRequest{
|
||||
Name: memo.Name,
|
||||
Comment: &apiv1.Memo{
|
||||
Content: fmt.Sprintf("Comment %d", i),
|
||||
Visibility: apiv1.Visibility_PUBLIC,
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
// List activities with page size 10 (as admin or userOne)
|
||||
// Activities are visible to the receiver (UserOne)
|
||||
resp, err := ts.Service.ListActivities(userOneCtx, &apiv1.ListActivitiesRequest{
|
||||
PageSize: 10,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, resp.Activities, 10)
|
||||
require.NotEmpty(t, resp.NextPageToken)
|
||||
|
||||
// List next page
|
||||
resp, err = ts.Service.ListActivities(userOneCtx, &apiv1.ListActivitiesRequest{
|
||||
PageSize: 10,
|
||||
PageToken: resp.NextPageToken,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, resp.Activities, 5)
|
||||
require.Empty(t, resp.NextPageToken)
|
||||
}
|
||||
|
|
@ -42,6 +42,10 @@ type Activity struct {
|
|||
type FindActivity struct {
|
||||
ID *int32
|
||||
Type *ActivityType
|
||||
|
||||
// Pagination
|
||||
Limit *int
|
||||
Offset *int
|
||||
}
|
||||
|
||||
func (s *Store) CreateActivity(ctx context.Context, create *Activity) (*Activity, error) {
|
||||
|
|
|
|||
|
|
@ -2,8 +2,10 @@ package mysql
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"google.golang.org/protobuf/encoding/protojson"
|
||||
|
||||
|
|
@ -56,6 +58,13 @@ func (d *DB) ListActivities(ctx context.Context, find *store.FindActivity) ([]*s
|
|||
}
|
||||
|
||||
query := "SELECT `id`, `creator_id`, `type`, `level`, `payload`, UNIX_TIMESTAMP(`created_ts`) FROM `activity` WHERE " + strings.Join(where, " AND ") + " ORDER BY `created_ts` DESC"
|
||||
if find.Limit != nil {
|
||||
query = fmt.Sprintf("%s LIMIT %d", query, *find.Limit)
|
||||
if find.Offset != nil {
|
||||
query = fmt.Sprintf("%s OFFSET %d", query, *find.Offset)
|
||||
}
|
||||
}
|
||||
|
||||
rows, err := d.db.QueryContext(ctx, query, args...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
|||
|
|
@ -2,8 +2,10 @@ package postgres
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"google.golang.org/protobuf/encoding/protojson"
|
||||
|
||||
|
|
@ -44,6 +46,13 @@ func (d *DB) ListActivities(ctx context.Context, find *store.FindActivity) ([]*s
|
|||
}
|
||||
|
||||
query := "SELECT id, creator_id, type, level, payload, created_ts FROM activity WHERE " + strings.Join(where, " AND ") + " ORDER BY created_ts DESC"
|
||||
if find.Limit != nil {
|
||||
query = fmt.Sprintf("%s LIMIT %d", query, *find.Limit)
|
||||
if find.Offset != nil {
|
||||
query = fmt.Sprintf("%s OFFSET %d", query, *find.Offset)
|
||||
}
|
||||
}
|
||||
|
||||
rows, err := d.db.QueryContext(ctx, query, args...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
|||
|
|
@ -2,8 +2,10 @@ package sqlite
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"google.golang.org/protobuf/encoding/protojson"
|
||||
|
||||
|
|
@ -46,6 +48,13 @@ func (d *DB) ListActivities(ctx context.Context, find *store.FindActivity) ([]*s
|
|||
}
|
||||
|
||||
query := "SELECT `id`, `creator_id`, `type`, `level`, `payload`, `created_ts` FROM `activity` WHERE " + strings.Join(where, " AND ") + " ORDER BY `created_ts` DESC"
|
||||
if find.Limit != nil {
|
||||
query = fmt.Sprintf("%s LIMIT %d", query, *find.Limit)
|
||||
if find.Offset != nil {
|
||||
query = fmt.Sprintf("%s OFFSET %d", query, *find.Offset)
|
||||
}
|
||||
}
|
||||
|
||||
rows, err := d.db.QueryContext(ctx, query, args...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
|||
Loading…
Reference in New Issue