Commit be5e24c0 authored by johnnyjoy's avatar johnnyjoy

refactor: renovate list memos endpoint

parent 14c72fa7
......@@ -15,3 +15,9 @@ message PageToken {
int32 limit = 1;
int32 offset = 2;
}
enum Direction {
DIRECTION_UNSPECIFIED = 0;
ASC = 1;
DESC = 2;
}
......@@ -26,7 +26,10 @@ service MemoService {
}
// ListMemos lists memos with pagination and filter.
rpc ListMemos(ListMemosRequest) returns (ListMemosResponse) {
option (google.api.http) = {get: "/api/v1/memos"};
option (google.api.http) = {
get: "/api/v1/memos"
additional_bindings: {get: "/api/v1/{parent=users/*}/memos"}
};
}
// GetMemo gets a memo.
rpc GetMemo(GetMemoRequest) returns (Memo) {
......@@ -190,16 +193,36 @@ message CreateMemoRequest {
}
message ListMemosRequest {
// The parent is the owner of the memos.
// If not specified or `users/-`, it will list all memos.
string parent = 1;
// The maximum number of memos to return.
int32 page_size = 1;
int32 page_size = 2;
// A page token, received from a previous `ListMemos` call.
// Provide this to retrieve the subsequent page.
string page_token = 2;
string page_token = 3;
// The state of the memos to list.
// Default to `NORMAL`. Set to `ARCHIVED` to list archived memos.
State state = 4;
// What field to sort the results by.
// Default to display_time.
string sort = 5;
// The direction to sort the results by.
// Default to DESC.
Direction direction = 6;
// Filter is a CEL expression to filter memos.
// Refer to `Shortcut.filter`.
string filter = 7;
// Filter is used to filter memos returned in the list.
// [Deprecated] Old filter contains some specific conditions to filter memos.
// Format: "creator == 'users/{user}' && visibilities == ['PUBLIC', 'PROTECTED']"
string filter = 3;
string old_filter = 8;
}
message ListMemosResponse {
......
......@@ -70,6 +70,55 @@ func (State) EnumDescriptor() ([]byte, []int) {
return file_api_v1_common_proto_rawDescGZIP(), []int{0}
}
type Direction int32
const (
Direction_DIRECTION_UNSPECIFIED Direction = 0
Direction_ASC Direction = 1
Direction_DESC Direction = 2
)
// Enum value maps for Direction.
var (
Direction_name = map[int32]string{
0: "DIRECTION_UNSPECIFIED",
1: "ASC",
2: "DESC",
}
Direction_value = map[string]int32{
"DIRECTION_UNSPECIFIED": 0,
"ASC": 1,
"DESC": 2,
}
)
func (x Direction) Enum() *Direction {
p := new(Direction)
*p = x
return p
}
func (x Direction) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (Direction) Descriptor() protoreflect.EnumDescriptor {
return file_api_v1_common_proto_enumTypes[1].Descriptor()
}
func (Direction) Type() protoreflect.EnumType {
return &file_api_v1_common_proto_enumTypes[1]
}
func (x Direction) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use Direction.Descriptor instead.
func (Direction) EnumDescriptor() ([]byte, []int) {
return file_api_v1_common_proto_rawDescGZIP(), []int{1}
}
// Used internally for obfuscating the page token.
type PageToken struct {
state protoimpl.MessageState `protogen:"open.v1"`
......@@ -135,18 +184,22 @@ var file_api_v1_common_proto_rawDesc = string([]byte{
0x0a, 0x05, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x15, 0x0a, 0x11, 0x53, 0x54, 0x41, 0x54, 0x45,
0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0a,
0x0a, 0x06, 0x4e, 0x4f, 0x52, 0x4d, 0x41, 0x4c, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x41, 0x52,
0x43, 0x48, 0x49, 0x56, 0x45, 0x44, 0x10, 0x02, 0x42, 0xa3, 0x01, 0x0a, 0x10, 0x63, 0x6f, 0x6d,
0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x42, 0x0b, 0x43,
0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x30, 0x67, 0x69,
0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x75, 0x73, 0x65, 0x6d, 0x65, 0x6d, 0x6f,
0x73, 0x2f, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x65,
0x6e, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x3b, 0x61, 0x70, 0x69, 0x76, 0x31, 0xa2, 0x02,
0x03, 0x4d, 0x41, 0x58, 0xaa, 0x02, 0x0c, 0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x41, 0x70, 0x69,
0x2e, 0x56, 0x31, 0xca, 0x02, 0x0c, 0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x5c, 0x41, 0x70, 0x69, 0x5c,
0x56, 0x31, 0xe2, 0x02, 0x18, 0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x5c, 0x41, 0x70, 0x69, 0x5c, 0x56,
0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x0e,
0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x3a, 0x3a, 0x41, 0x70, 0x69, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x43, 0x48, 0x49, 0x56, 0x45, 0x44, 0x10, 0x02, 0x2a, 0x39, 0x0a, 0x09, 0x44, 0x69, 0x72, 0x65,
0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x19, 0x0a, 0x15, 0x44, 0x49, 0x52, 0x45, 0x43, 0x54, 0x49,
0x4f, 0x4e, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00,
0x12, 0x07, 0x0a, 0x03, 0x41, 0x53, 0x43, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x44, 0x45, 0x53,
0x43, 0x10, 0x02, 0x42, 0xa3, 0x01, 0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x2e, 0x6d, 0x65, 0x6d, 0x6f,
0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x42, 0x0b, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e,
0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e,
0x63, 0x6f, 0x6d, 0x2f, 0x75, 0x73, 0x65, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2f, 0x6d, 0x65, 0x6d,
0x6f, 0x73, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x61, 0x70, 0x69,
0x2f, 0x76, 0x31, 0x3b, 0x61, 0x70, 0x69, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x4d, 0x41, 0x58, 0xaa,
0x02, 0x0c, 0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x41, 0x70, 0x69, 0x2e, 0x56, 0x31, 0xca, 0x02,
0x0c, 0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x5c, 0x41, 0x70, 0x69, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x18,
0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x5c, 0x41, 0x70, 0x69, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42,
0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x0e, 0x4d, 0x65, 0x6d, 0x6f, 0x73,
0x3a, 0x3a, 0x41, 0x70, 0x69, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x33,
})
var (
......@@ -161,11 +214,12 @@ func file_api_v1_common_proto_rawDescGZIP() []byte {
return file_api_v1_common_proto_rawDescData
}
var file_api_v1_common_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
var file_api_v1_common_proto_enumTypes = make([]protoimpl.EnumInfo, 2)
var file_api_v1_common_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
var file_api_v1_common_proto_goTypes = []any{
(State)(0), // 0: memos.api.v1.State
(*PageToken)(nil), // 1: memos.api.v1.PageToken
(Direction)(0), // 1: memos.api.v1.Direction
(*PageToken)(nil), // 2: memos.api.v1.PageToken
}
var file_api_v1_common_proto_depIdxs = []int32{
0, // [0:0] is the sub-list for method output_type
......@@ -185,7 +239,7 @@ func file_api_v1_common_proto_init() {
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_api_v1_common_proto_rawDesc), len(file_api_v1_common_proto_rawDesc)),
NumEnums: 1,
NumEnums: 2,
NumMessages: 1,
NumExtensions: 0,
NumServices: 0,
......
This diff is collapsed.
......@@ -91,6 +91,56 @@ func local_request_MemoService_ListMemos_0(ctx context.Context, marshaler runtim
return msg, metadata, err
}
var filter_MemoService_ListMemos_1 = &utilities.DoubleArray{Encoding: map[string]int{"parent": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}}
func request_MemoService_ListMemos_1(ctx context.Context, marshaler runtime.Marshaler, client MemoServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq ListMemosRequest
metadata runtime.ServerMetadata
err error
)
val, ok := pathParams["parent"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "parent")
}
protoReq.Parent, err = runtime.String(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "parent", err)
}
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_MemoService_ListMemos_1); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := client.ListMemos(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_MemoService_ListMemos_1(ctx context.Context, marshaler runtime.Marshaler, server MemoServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq ListMemosRequest
metadata runtime.ServerMetadata
err error
)
val, ok := pathParams["parent"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "parent")
}
protoReq.Parent, err = runtime.String(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "parent", err)
}
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_MemoService_ListMemos_1); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.ListMemos(ctx, &protoReq)
return msg, metadata, err
}
func request_MemoService_GetMemo_0(ctx context.Context, marshaler runtime.Marshaler, client MemoServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq GetMemoRequest
......@@ -743,6 +793,26 @@ func RegisterMemoServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux
}
forward_MemoService_ListMemos_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodGet, pattern_MemoService_ListMemos_1, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v1.MemoService/ListMemos", runtime.WithHTTPPathPattern("/api/v1/{parent=users/*}/memos"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_MemoService_ListMemos_1(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_MemoService_ListMemos_1(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodGet, pattern_MemoService_GetMemo_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
......@@ -1097,6 +1167,23 @@ func RegisterMemoServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux
}
forward_MemoService_ListMemos_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodGet, pattern_MemoService_ListMemos_1, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/memos.api.v1.MemoService/ListMemos", runtime.WithHTTPPathPattern("/api/v1/{parent=users/*}/memos"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_MemoService_ListMemos_1(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_MemoService_ListMemos_1(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodGet, pattern_MemoService_GetMemo_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
......@@ -1341,6 +1428,7 @@ func RegisterMemoServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux
var (
pattern_MemoService_CreateMemo_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "memos"}, ""))
pattern_MemoService_ListMemos_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "memos"}, ""))
pattern_MemoService_ListMemos_1 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "users", "parent", "memos"}, ""))
pattern_MemoService_GetMemo_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3}, []string{"api", "v1", "memos", "name"}, ""))
pattern_MemoService_UpdateMemo_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3}, []string{"api", "v1", "memos", "memo.name"}, ""))
pattern_MemoService_DeleteMemo_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3}, []string{"api", "v1", "memos", "name"}, ""))
......@@ -1360,6 +1448,7 @@ var (
var (
forward_MemoService_CreateMemo_0 = runtime.ForwardResponseMessage
forward_MemoService_ListMemos_0 = runtime.ForwardResponseMessage
forward_MemoService_ListMemos_1 = runtime.ForwardResponseMessage
forward_MemoService_GetMemo_0 = runtime.ForwardResponseMessage
forward_MemoService_UpdateMemo_0 = runtime.ForwardResponseMessage
forward_MemoService_DeleteMemo_0 = runtime.ForwardResponseMessage
......
......@@ -304,6 +304,13 @@ paths:
schema:
$ref: '#/definitions/googlerpcStatus'
parameters:
- name: parent
description: |-
The parent is the owner of the memos.
If not specified or `users/-`, it will list all memos.
in: query
required: false
type: string
- name: pageSize
description: The maximum number of memos to return.
in: query
......@@ -317,9 +324,47 @@ paths:
in: query
required: false
type: string
- name: state
description: |-
The state of the memos to list.
Default to `NORMAL`. Set to `ARCHIVED` to list archived memos.
in: query
required: false
type: string
enum:
- STATE_UNSPECIFIED
- NORMAL
- ARCHIVED
default: STATE_UNSPECIFIED
- name: sort
description: |-
What field to sort the results by.
Default to display_time.
in: query
required: false
type: string
- name: direction
description: |-
The direction to sort the results by.
Default to DESC.
in: query
required: false
type: string
enum:
- DIRECTION_UNSPECIFIED
- ASC
- DESC
default: DIRECTION_UNSPECIFIED
- name: filter
description: |-
Filter is used to filter memos returned in the list.
Filter is a CEL expression to filter memos.
Refer to `Shortcut.filter`.
in: query
required: false
type: string
- name: oldFilter
description: |-
[Deprecated] Old filter contains some specific conditions to filter memos.
Format: "creator == 'users/{user}' && visibilities == ['PUBLIC', 'PROTECTED']"
in: query
required: false
......@@ -1406,6 +1451,88 @@ paths:
pattern: users/[^/]+
tags:
- UserService
/api/v1/{parent}/memos:
get:
summary: ListMemos lists memos with pagination and filter.
operationId: MemoService_ListMemos2
responses:
"200":
description: A successful response.
schema:
$ref: '#/definitions/v1ListMemosResponse'
default:
description: An unexpected error response.
schema:
$ref: '#/definitions/googlerpcStatus'
parameters:
- name: parent
description: |-
The parent is the owner of the memos.
If not specified or `users/-`, it will list all memos.
in: path
required: true
type: string
pattern: users/[^/]+
- name: pageSize
description: The maximum number of memos to return.
in: query
required: false
type: integer
format: int32
- name: pageToken
description: |-
A page token, received from a previous `ListMemos` call.
Provide this to retrieve the subsequent page.
in: query
required: false
type: string
- name: state
description: |-
The state of the memos to list.
Default to `NORMAL`. Set to `ARCHIVED` to list archived memos.
in: query
required: false
type: string
enum:
- STATE_UNSPECIFIED
- NORMAL
- ARCHIVED
default: STATE_UNSPECIFIED
- name: sort
description: |-
What field to sort the results by.
Default to display_time.
in: query
required: false
type: string
- name: direction
description: |-
The direction to sort the results by.
Default to DESC.
in: query
required: false
type: string
enum:
- DIRECTION_UNSPECIFIED
- ASC
- DESC
default: DIRECTION_UNSPECIFIED
- name: filter
description: |-
Filter is a CEL expression to filter memos.
Refer to `Shortcut.filter`.
in: query
required: false
type: string
- name: oldFilter
description: |-
[Deprecated] Old filter contains some specific conditions to filter memos.
Format: "creator == 'users/{user}' && visibilities == ['PUBLIC', 'PROTECTED']"
in: query
required: false
type: string
tags:
- MemoService
/api/v1/{parent}/shortcuts:
get:
summary: ListShortcuts returns a list of shortcuts for a user.
......@@ -2472,6 +2599,13 @@ definitions:
type: string
url:
type: string
v1Direction:
type: string
enum:
- DIRECTION_UNSPECIFIED
- ASC
- DESC
default: DIRECTION_UNSPECIFIED
v1EmbeddedContentNode:
type: object
properties:
......
......@@ -12,6 +12,7 @@ var authenticationAllowlistMethods = map[string]bool{
"/memos.api.v1.AuthService/SignOut": true,
"/memos.api.v1.AuthService/SignUp": true,
"/memos.api.v1.UserService/GetUser": true,
"/memos.api.v1.UserService/GetUserByUsername": true,
"/memos.api.v1.UserService/GetUserAvatarBinary": true,
"/memos.api.v1.UserService/ListAllUserStats": true,
"/memos.api.v1.UserService/SearchUsers": true,
......
......@@ -99,9 +99,42 @@ func (s *APIV1Service) ListMemos(ctx context.Context, request *v1pb.ListMemosReq
// Exclude comments by default.
ExcludeComments: true,
}
if err := s.buildMemoFindWithFilter(ctx, memoFind, request.Filter); err != nil {
if err := s.buildMemoFindWithFilter(ctx, memoFind, request.OldFilter); err != nil {
return nil, status.Errorf(codes.InvalidArgument, "failed to build find memos with filter: %v", err)
}
if request.Parent != "" && request.Parent != "users/-" {
userID, err := ExtractUserIDFromName(request.Parent)
if err != nil {
return nil, status.Errorf(codes.InvalidArgument, "invalid parent: %v", err)
}
memoFind.CreatorID = &userID
memoFind.OrderByPinned = true
}
if request.Direction == v1pb.Direction_ASC {
memoFind.OrderByTimeAsc = true
}
if request.Filter != "" {
memoFind.Filter = &request.Filter
}
currentUser, err := s.GetCurrentUser(ctx)
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to get user")
}
if currentUser == nil {
memoFind.VisibilityList = []store.Visibility{store.Public}
} else {
if memoFind.CreatorID == nil || *memoFind.CreatorID != currentUser.ID {
memoFind.VisibilityList = []store.Visibility{store.Public, store.Protected}
}
}
if request.State == v1pb.State_ARCHIVED {
state := store.Archived
memoFind.RowStatus = &state
} else {
state := store.Normal
memoFind.RowStatus = &state
}
var limit, offset int
if request.PageToken != "" {
......
......@@ -9,7 +9,6 @@ import (
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
apiv1 "github.com/usememos/memos/proto/gen/api/v1"
"github.com/usememos/memos/store"
)
......@@ -25,21 +24,12 @@ func (s *APIV1Service) buildMemoFindWithFilter(ctx context.Context, find *store.
if len(filterExpr.ContentSearch) > 0 {
find.ContentSearch = filterExpr.ContentSearch
}
if len(filterExpr.Visibilities) > 0 {
find.VisibilityList = filterExpr.Visibilities
}
if filterExpr.TagSearch != nil {
if find.PayloadFind == nil {
find.PayloadFind = &store.FindMemoPayload{}
}
find.PayloadFind.TagSearch = filterExpr.TagSearch
}
if filterExpr.OrderByPinned {
find.OrderByPinned = filterExpr.OrderByPinned
}
if filterExpr.OrderByTimeAsc {
find.OrderByTimeAsc = filterExpr.OrderByTimeAsc
}
if filterExpr.DisplayTimeAfter != nil {
workspaceMemoRelatedSetting, err := s.Store.GetWorkspaceMemoRelatedSetting(ctx)
if err != nil {
......@@ -62,31 +52,6 @@ func (s *APIV1Service) buildMemoFindWithFilter(ctx context.Context, find *store.
find.CreatedTsBefore = filterExpr.DisplayTimeBefore
}
}
if filterExpr.Creator != nil {
userID, err := ExtractUserIDFromName(*filterExpr.Creator)
if err != nil {
return errors.Wrap(err, "invalid user name")
}
user, err := s.Store.GetUser(ctx, &store.FindUser{
ID: &userID,
})
if err != nil {
return status.Errorf(codes.Internal, "failed to get user")
}
if user == nil {
return status.Errorf(codes.NotFound, "user not found")
}
find.CreatorID = &user.ID
}
if filterExpr.RowStatus != nil {
find.RowStatus = filterExpr.RowStatus
}
if filterExpr.Random {
find.Random = filterExpr.Random
}
if filterExpr.Limit != nil {
find.Limit = filterExpr.Limit
}
if filterExpr.IncludeComments {
find.ExcludeComments = false
}
......@@ -104,23 +69,6 @@ func (s *APIV1Service) buildMemoFindWithFilter(ctx context.Context, find *store.
}
}
user, err := s.GetCurrentUser(ctx)
if err != nil {
return status.Errorf(codes.Internal, "failed to get current user")
}
// If the user is not authenticated, only public memos are visible.
if user == nil {
if filter == "" {
// If no filter is provided, return an error.
return status.Errorf(codes.InvalidArgument, "filter is required for unauthenticated user")
}
find.VisibilityList = []store.Visibility{store.Public}
} else if find.CreatorID == nil || *find.CreatorID != user.ID {
// If creator is not specified or the creator is not the current user, only public and protected memos are visible.
find.VisibilityList = []store.Visibility{store.Public, store.Protected}
}
workspaceMemoRelatedSetting, err := s.Store.GetWorkspaceMemoRelatedSetting(ctx)
if err != nil {
return status.Errorf(codes.Internal, "failed to get workspace memo related setting")
......@@ -134,16 +82,9 @@ func (s *APIV1Service) buildMemoFindWithFilter(ctx context.Context, find *store.
// MemoFilterCELAttributes are the CEL attributes.
var MemoFilterCELAttributes = []cel.EnvOption{
cel.Variable("content_search", cel.ListType(cel.StringType)),
cel.Variable("visibilities", cel.ListType(cel.StringType)),
cel.Variable("tag_search", cel.ListType(cel.StringType)),
cel.Variable("order_by_pinned", cel.BoolType),
cel.Variable("order_by_time_asc", cel.BoolType),
cel.Variable("display_time_before", cel.IntType),
cel.Variable("display_time_after", cel.IntType),
cel.Variable("creator", cel.StringType),
cel.Variable("state", cel.StringType),
cel.Variable("random", cel.BoolType),
cel.Variable("limit", cel.IntType),
cel.Variable("include_comments", cel.BoolType),
cel.Variable("has_link", cel.BoolType),
cel.Variable("has_task_list", cel.BoolType),
......@@ -153,16 +94,9 @@ var MemoFilterCELAttributes = []cel.EnvOption{
type MemoFilter struct {
ContentSearch []string
Visibilities []store.Visibility
TagSearch []string
OrderByPinned bool
OrderByTimeAsc bool
DisplayTimeBefore *int64
DisplayTimeAfter *int64
Creator *string
RowStatus *store.RowStatus
Random bool
Limit *int
IncludeComments bool
HasLink bool
HasTaskList bool
......@@ -200,13 +134,6 @@ func findMemoField(callExpr *exprv1.Expr_Call, filter *MemoFilter) {
contentSearch = append(contentSearch, value)
}
filter.ContentSearch = contentSearch
} else if idExpr.Name == "visibilities" {
visibilities := []store.Visibility{}
for _, expr := range callExpr.Args[1].GetListExpr().GetElements() {
value := expr.GetConstExpr().GetStringValue()
visibilities = append(visibilities, store.Visibility(value))
}
filter.Visibilities = visibilities
} else if idExpr.Name == "tag_search" {
tagSearch := []string{}
for _, expr := range callExpr.Args[1].GetListExpr().GetElements() {
......@@ -214,30 +141,12 @@ func findMemoField(callExpr *exprv1.Expr_Call, filter *MemoFilter) {
tagSearch = append(tagSearch, value)
}
filter.TagSearch = tagSearch
} else if idExpr.Name == "order_by_pinned" {
value := callExpr.Args[1].GetConstExpr().GetBoolValue()
filter.OrderByPinned = value
} else if idExpr.Name == "order_by_time_asc" {
value := callExpr.Args[1].GetConstExpr().GetBoolValue()
filter.OrderByTimeAsc = value
} else if idExpr.Name == "display_time_before" {
displayTimeBefore := callExpr.Args[1].GetConstExpr().GetInt64Value()
filter.DisplayTimeBefore = &displayTimeBefore
} else if idExpr.Name == "display_time_after" {
displayTimeAfter := callExpr.Args[1].GetConstExpr().GetInt64Value()
filter.DisplayTimeAfter = &displayTimeAfter
} else if idExpr.Name == "creator" {
creator := callExpr.Args[1].GetConstExpr().GetStringValue()
filter.Creator = &creator
} else if idExpr.Name == "state" {
state := convertStateToStore(apiv1.State(apiv1.State_value[callExpr.Args[1].GetConstExpr().GetStringValue()]))
filter.RowStatus = &state
} else if idExpr.Name == "random" {
value := callExpr.Args[1].GetConstExpr().GetBoolValue()
filter.Random = value
} else if idExpr.Name == "limit" {
limit := int(callExpr.Args[1].GetConstExpr().GetInt64Value())
filter.Limit = &limit
} else if idExpr.Name == "include_comments" {
value := callExpr.Args[1].GetConstExpr().GetBoolValue()
filter.IncludeComments = value
......
......@@ -45,13 +45,14 @@ const AddMemoRelationPopover = (props: Props) => {
setIsFetching(true);
try {
const filters = [`creator == "${user.name}"`, `state == "NORMAL"`];
const conditions = [];
if (searchText) {
filters.push(`content_search == [${JSON.stringify(searchText)}]`);
conditions.push(`content_search == [${JSON.stringify(searchText)}]`);
}
const { memos } = await memoServiceClient.listMemos({
parent: user.name,
pageSize: DEFAULT_LIST_MEMOS_PAGE_SIZE,
filter: filters.length > 0 ? filters.join(" && ") : undefined,
oldFilter: conditions.length > 0 ? conditions.join(" && ") : undefined,
});
setFetchedMemos(memos);
} catch (error: any) {
......
......@@ -5,6 +5,7 @@ import PullToRefresh from "react-simple-pull-to-refresh";
import { DEFAULT_LIST_MEMOS_PAGE_SIZE } from "@/helpers/consts";
import useResponsiveWidth from "@/hooks/useResponsiveWidth";
import { useMemoList, useMemoStore } from "@/store/v1";
import { Direction, State } from "@/types/proto/api/v1/common";
import { Memo } from "@/types/proto/api/v1/memo_service";
import { useTranslate } from "@/utils/i18n";
import Empty from "../Empty";
......@@ -12,11 +13,14 @@ import Empty from "../Empty";
interface Props {
renderer: (memo: Memo) => JSX.Element;
listSort?: (list: Memo[]) => Memo[];
filter?: string;
owner?: string;
state?: State;
direction?: Direction;
oldFilter?: string;
pageSize?: number;
}
interface State {
interface LocalState {
isRequesting: boolean;
nextPageToken: string;
}
......@@ -26,7 +30,7 @@ const PagedMemoList = (props: Props) => {
const { md } = useResponsiveWidth();
const memoStore = useMemoStore();
const memoList = useMemoList();
const [state, setState] = useState<State>({
const [state, setState] = useState<LocalState>({
isRequesting: true, // Initial request
nextPageToken: "",
});
......@@ -35,7 +39,10 @@ const PagedMemoList = (props: Props) => {
const fetchMoreMemos = async (nextPageToken: string) => {
setState((state) => ({ ...state, isRequesting: true }));
const response = await memoStore.fetchMemos({
filter: props.filter || "",
parent: props.owner || "",
state: props.state || State.NORMAL,
direction: props.direction || Direction.DESC,
oldFilter: props.oldFilter || "",
pageSize: props.pageSize || DEFAULT_LIST_MEMOS_PAGE_SIZE,
pageToken: nextPageToken,
});
......@@ -53,7 +60,7 @@ const PagedMemoList = (props: Props) => {
useEffect(() => {
refreshList();
}, [props.filter, props.pageSize]);
}, [props.owner, props.state, props.direction, props.oldFilter, props.pageSize]);
const children = (
<div className="flex flex-col justify-start items-start w-full max-w-full">
......
......@@ -8,7 +8,7 @@ import PagedMemoList from "@/components/PagedMemoList";
import SearchBar from "@/components/SearchBar";
import useCurrentUser from "@/hooks/useCurrentUser";
import { useMemoFilterStore } from "@/store/v1";
import { State } from "@/types/proto/api/v1/common";
import { Direction, State } from "@/types/proto/api/v1/common";
import { Memo } from "@/types/proto/api/v1/memo_service";
import { useTranslate } from "@/utils/i18n";
......@@ -18,7 +18,7 @@ const Archived = () => {
const memoFilterStore = useMemoFilterStore();
const memoListFilter = useMemo(() => {
const filters = [`creator == "${user.name}"`, `state == "ARCHIVED"`];
const conditions = [];
const contentSearch: string[] = [];
const tagSearch: string[] = [];
for (const filter of memoFilterStore.filters) {
......@@ -28,16 +28,13 @@ const Archived = () => {
tagSearch.push(`"${filter.value}"`);
}
}
if (memoFilterStore.orderByTimeAsc) {
filters.push(`order_by_time_asc == true`);
}
if (contentSearch.length > 0) {
filters.push(`content_search == [${contentSearch.join(", ")}]`);
conditions.push(`content_search == [${contentSearch.join(", ")}]`);
}
if (tagSearch.length > 0) {
filters.push(`tag_search == [${tagSearch.join(", ")}]`);
conditions.push(`tag_search == [${tagSearch.join(", ")}]`);
}
return filters.join(" && ");
return conditions.join(" && ");
}, [user, memoFilterStore.filters]);
return (
......@@ -66,7 +63,10 @@ const Archived = () => {
: dayjs(b.displayTime).unix() - dayjs(a.displayTime).unix(),
)
}
filter={memoListFilter}
owner={user.name}
state={State.ARCHIVED}
direction={memoFilterStore.orderByTimeAsc ? Direction.ASC : Direction.DESC}
oldFilter={memoListFilter}
/>
</div>
</div>
......
......@@ -8,7 +8,7 @@ import PagedMemoList from "@/components/PagedMemoList";
import useCurrentUser from "@/hooks/useCurrentUser";
import useResponsiveWidth from "@/hooks/useResponsiveWidth";
import { useMemoFilterStore } from "@/store/v1";
import { State } from "@/types/proto/api/v1/common";
import { Direction, State } from "@/types/proto/api/v1/common";
import { Memo } from "@/types/proto/api/v1/memo_service";
import { cn } from "@/utils";
......@@ -18,7 +18,7 @@ const Explore = () => {
const memoFilterStore = useMemoFilterStore();
const memoListFilter = useMemo(() => {
const filters = [`state == "NORMAL"`, `visibilities == [${user ? "'PUBLIC', 'PROTECTED'" : "'PUBLIC'"}]`];
const conditions = [];
const contentSearch: string[] = [];
const tagSearch: string[] = [];
for (const filter of memoFilterStore.filters) {
......@@ -27,29 +27,26 @@ const Explore = () => {
} else if (filter.factor === "tagSearch") {
tagSearch.push(`"${filter.value}"`);
} else if (filter.factor === "property.hasLink") {
filters.push(`has_link == true`);
conditions.push(`has_link == true`);
} else if (filter.factor === "property.hasTaskList") {
filters.push(`has_task_list == true`);
conditions.push(`has_task_list == true`);
} else if (filter.factor === "property.hasCode") {
filters.push(`has_code == true`);
conditions.push(`has_code == true`);
} else if (filter.factor === "displayTime") {
const filterDate = new Date(filter.value);
const filterUtcTimestamp = filterDate.getTime() + filterDate.getTimezoneOffset() * 60 * 1000;
const timestampAfter = filterUtcTimestamp / 1000;
filters.push(`display_time_after == ${timestampAfter}`);
filters.push(`display_time_before == ${timestampAfter + 60 * 60 * 24}`);
conditions.push(`display_time_after == ${timestampAfter}`);
conditions.push(`display_time_before == ${timestampAfter + 60 * 60 * 24}`);
}
}
if (memoFilterStore.orderByTimeAsc) {
filters.push(`order_by_time_asc == true`);
}
if (contentSearch.length > 0) {
filters.push(`content_search == [${contentSearch.join(", ")}]`);
conditions.push(`content_search == [${contentSearch.join(", ")}]`);
}
if (tagSearch.length > 0) {
filters.push(`tag_search == [${tagSearch.join(", ")}]`);
conditions.push(`tag_search == [${tagSearch.join(", ")}]`);
}
return filters.join(" && ");
return conditions.join(" && ");
}, [user, memoFilterStore.filters, memoFilterStore.orderByTimeAsc]);
return (
......@@ -74,7 +71,8 @@ const Explore = () => {
: dayjs(b.displayTime).unix() - dayjs(a.displayTime).unix(),
)
}
filter={memoListFilter}
direction={memoFilterStore.orderByTimeAsc ? Direction.ASC : Direction.DESC}
oldFilter={memoListFilter}
/>
</div>
</div>
......
......@@ -9,7 +9,7 @@ import PagedMemoList from "@/components/PagedMemoList";
import useCurrentUser from "@/hooks/useCurrentUser";
import useResponsiveWidth from "@/hooks/useResponsiveWidth";
import { useMemoFilterStore } from "@/store/v1";
import { State } from "@/types/proto/api/v1/common";
import { Direction, State } from "@/types/proto/api/v1/common";
import { Memo } from "@/types/proto/api/v1/memo_service";
import { cn } from "@/utils";
......@@ -19,7 +19,7 @@ const Home = () => {
const memoFilterStore = useMemoFilterStore();
const memoListFilter = useMemo(() => {
const filters = [`creator == "${user.name}"`, `state == "NORMAL"`, `order_by_pinned == true`];
const conditions = [];
const contentSearch: string[] = [];
const tagSearch: string[] = [];
for (const filter of memoFilterStore.filters) {
......@@ -28,29 +28,29 @@ const Home = () => {
} else if (filter.factor === "tagSearch") {
tagSearch.push(`"${filter.value}"`);
} else if (filter.factor === "property.hasLink") {
filters.push(`has_link == true`);
conditions.push(`has_link == true`);
} else if (filter.factor === "property.hasTaskList") {
filters.push(`has_task_list == true`);
conditions.push(`has_task_list == true`);
} else if (filter.factor === "property.hasCode") {
filters.push(`has_code == true`);
conditions.push(`has_code == true`);
} else if (filter.factor === "displayTime") {
const filterDate = new Date(filter.value);
const filterUtcTimestamp = filterDate.getTime() + filterDate.getTimezoneOffset() * 60 * 1000;
const timestampAfter = filterUtcTimestamp / 1000;
filters.push(`display_time_after == ${timestampAfter}`);
filters.push(`display_time_before == ${timestampAfter + 60 * 60 * 24}`);
conditions.push(`display_time_after == ${timestampAfter}`);
conditions.push(`display_time_before == ${timestampAfter + 60 * 60 * 24}`);
}
}
if (memoFilterStore.orderByTimeAsc) {
filters.push(`order_by_time_asc == true`);
conditions.push(`order_by_time_asc == true`);
}
if (contentSearch.length > 0) {
filters.push(`content_search == [${contentSearch.join(", ")}]`);
conditions.push(`content_search == [${contentSearch.join(", ")}]`);
}
if (tagSearch.length > 0) {
filters.push(`tag_search == [${tagSearch.join(", ")}]`);
conditions.push(`tag_search == [${tagSearch.join(", ")}]`);
}
return filters.join(" && ");
return conditions.join(" && ");
}, [user, memoFilterStore.filters, memoFilterStore.orderByTimeAsc]);
return (
......@@ -77,7 +77,9 @@ const Home = () => {
)
.sort((a, b) => Number(b.pinned) - Number(a.pinned))
}
filter={memoListFilter}
owner={user.name}
direction={memoFilterStore.orderByTimeAsc ? Direction.ASC : Direction.DESC}
oldFilter={memoListFilter}
/>
</div>
</div>
......
......@@ -12,7 +12,7 @@ import PagedMemoList from "@/components/PagedMemoList";
import UserAvatar from "@/components/UserAvatar";
import useLoading from "@/hooks/useLoading";
import { useMemoFilterStore, useUserStore } from "@/store/v1";
import { State } from "@/types/proto/api/v1/common";
import { Direction, State } from "@/types/proto/api/v1/common";
import { Memo } from "@/types/proto/api/v1/memo_service";
import { User } from "@/types/proto/api/v1/user_service";
import { useTranslate } from "@/utils/i18n";
......@@ -48,7 +48,7 @@ const UserProfile = () => {
return "";
}
const filters = [`creator == "${user.name}"`, `state == "NORMAL"`, `order_by_pinned == true`];
const conditions = [];
const contentSearch: string[] = [];
const tagSearch: string[] = [];
for (const filter of memoFilterStore.filters) {
......@@ -59,12 +59,12 @@ const UserProfile = () => {
}
}
if (contentSearch.length > 0) {
filters.push(`content_search == [${contentSearch.join(", ")}]`);
conditions.push(`content_search == [${contentSearch.join(", ")}]`);
}
if (tagSearch.length > 0) {
filters.push(`tag_search == [${tagSearch.join(", ")}]`);
conditions.push(`tag_search == [${tagSearch.join(", ")}]`);
}
return filters.join(" && ");
return conditions.join(" && ");
}, [user, memoFilterStore.filters]);
const handleCopyProfileLink = () => {
......@@ -115,7 +115,9 @@ const UserProfile = () => {
)
.sort((a, b) => Number(b.pinned) - Number(a.pinned))
}
filter={memoListFilter}
owner={user.name}
direction={memoFilterStore.orderByTimeAsc ? Direction.ASC : Direction.DESC}
oldFilter={memoListFilter}
/>
</>
) : (
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment