mirror of
https://github.com/usememos/memos.git
synced 2025-06-05 22:09:59 +02:00
feat: implement list memos filter
This commit is contained in:
@ -7,6 +7,7 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
apiv2pb "github.com/usememos/memos/proto/gen/api/v2"
|
||||
"github.com/usememos/memos/store"
|
||||
v1alpha1 "google.golang.org/genproto/googleapis/api/expr/v1alpha1"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
@ -26,19 +27,32 @@ func NewMemoService(store *store.Store) *MemoService {
|
||||
|
||||
func (s *MemoService) ListMemos(ctx context.Context, request *apiv2pb.ListMemosRequest) (*apiv2pb.ListMemosResponse, error) {
|
||||
memoFind := &store.FindMemo{}
|
||||
if request.Filter != "" {
|
||||
filter, err := parseListMemosFilter(request.Filter)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "invalid filter: %v", err)
|
||||
}
|
||||
if filter.Visibility != nil {
|
||||
memoFind.VisibilityList = []store.Visibility{*filter.Visibility}
|
||||
}
|
||||
if filter.CreatedTsBefore != nil {
|
||||
memoFind.CreatedTsBefore = filter.CreatedTsBefore
|
||||
}
|
||||
if filter.CreatedTsAfter != nil {
|
||||
memoFind.CreatedTsAfter = filter.CreatedTsAfter
|
||||
}
|
||||
}
|
||||
userIDPtr := ctx.Value(UserIDContextKey)
|
||||
// If the user is not authenticated, only public memos are visible.
|
||||
if userIDPtr == nil {
|
||||
memoFind.VisibilityList = []store.Visibility{store.Public}
|
||||
}
|
||||
if request.PageSize != 0 {
|
||||
offset := int(request.Page * request.PageSize)
|
||||
limit := int(request.PageSize)
|
||||
memoFind.Offset = &offset
|
||||
memoFind.Limit = &limit
|
||||
}
|
||||
if request.Filter != "" {
|
||||
visibilityString, err := getVisibilityFilter(request.Filter)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "invalid filter: %v", err)
|
||||
}
|
||||
memoFind.VisibilityList = []store.Visibility{store.Visibility(visibilityString)}
|
||||
}
|
||||
memos, err := s.Store.ListMemos(ctx, memoFind)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -49,9 +63,8 @@ func (s *MemoService) ListMemos(ctx context.Context, request *apiv2pb.ListMemosR
|
||||
memoMessages[i] = convertMemoFromStore(memo)
|
||||
}
|
||||
|
||||
// TODO(steven): Add privalige checks.
|
||||
response := &apiv2pb.ListMemosResponse{
|
||||
Memos: nil,
|
||||
Memos: memoMessages,
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
@ -83,39 +96,59 @@ func (s *MemoService) GetMemo(ctx context.Context, request *apiv2pb.GetMemoReque
|
||||
return response, nil
|
||||
}
|
||||
|
||||
// getVisibilityFilter will parse the simple filter such as `visibility = "PRIVATE"` to "PRIVATE" .
|
||||
func getVisibilityFilter(filter string) (string, error) {
|
||||
formatInvalidErr := errors.Errorf("invalid filter %q", filter)
|
||||
e, err := cel.NewEnv(cel.Variable("visibility", cel.StringType))
|
||||
// ListMemosFilterCELAttributes are the CEL attributes for ListMemosFilter.
|
||||
var ListMemosFilterCELAttributes = []cel.EnvOption{
|
||||
cel.Variable("visibility", cel.StringType),
|
||||
cel.Variable("created_ts_before", cel.IntType),
|
||||
cel.Variable("created_ts_after", cel.IntType),
|
||||
}
|
||||
|
||||
type ListMemosFilter struct {
|
||||
Visibility *store.Visibility
|
||||
CreatedTsBefore *int64
|
||||
CreatedTsAfter *int64
|
||||
}
|
||||
|
||||
func parseListMemosFilter(expression string) (*ListMemosFilter, error) {
|
||||
e, err := cel.NewEnv(ListMemosFilterCELAttributes...)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
ast, issues := e.Compile(filter)
|
||||
ast, issues := e.Compile(expression)
|
||||
if issues != nil {
|
||||
return "", status.Errorf(codes.InvalidArgument, issues.String())
|
||||
return nil, errors.Errorf("found issue %v", issues)
|
||||
}
|
||||
expr := ast.Expr()
|
||||
if expr == nil {
|
||||
return "", formatInvalidErr
|
||||
filter := &ListMemosFilter{}
|
||||
callExpr := ast.Expr().GetCallExpr()
|
||||
findField(callExpr, filter)
|
||||
return filter, nil
|
||||
}
|
||||
|
||||
func findField(callExpr *v1alpha1.Expr_Call, filter *ListMemosFilter) {
|
||||
if len(callExpr.Args) == 2 {
|
||||
idExpr := callExpr.Args[0].GetIdentExpr()
|
||||
if idExpr != nil {
|
||||
if idExpr.Name == "visibility" {
|
||||
visibility := store.Visibility(callExpr.Args[1].GetConstExpr().GetStringValue())
|
||||
filter.Visibility = &visibility
|
||||
}
|
||||
if idExpr.Name == "created_ts_before" {
|
||||
createdTsBefore := callExpr.Args[1].GetConstExpr().GetInt64Value()
|
||||
filter.CreatedTsBefore = &createdTsBefore
|
||||
}
|
||||
if idExpr.Name == "created_ts_after" {
|
||||
createdTsAfter := callExpr.Args[1].GetConstExpr().GetInt64Value()
|
||||
filter.CreatedTsAfter = &createdTsAfter
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
callExpr := expr.GetCallExpr()
|
||||
if callExpr == nil {
|
||||
return "", formatInvalidErr
|
||||
for _, arg := range callExpr.Args {
|
||||
callExpr := arg.GetCallExpr()
|
||||
if callExpr != nil {
|
||||
findField(callExpr, filter)
|
||||
}
|
||||
}
|
||||
if callExpr.Function != "_==_" {
|
||||
return "", formatInvalidErr
|
||||
}
|
||||
if len(callExpr.Args) != 2 {
|
||||
return "", formatInvalidErr
|
||||
}
|
||||
if callExpr.Args[0].GetIdentExpr() == nil || callExpr.Args[0].GetIdentExpr().Name != "visibility" {
|
||||
return "", formatInvalidErr
|
||||
}
|
||||
constExpr := callExpr.Args[1].GetConstExpr()
|
||||
if constExpr == nil {
|
||||
return "", formatInvalidErr
|
||||
}
|
||||
return constExpr.GetStringValue(), nil
|
||||
}
|
||||
|
||||
func convertMemoFromStore(memo *store.Memo) *apiv2pb.Memo {
|
||||
|
Reference in New Issue
Block a user