From 8d8e9b3b30df46737868de6c9e5e32350f690e32 Mon Sep 17 00:00:00 2001 From: Steven Date: Mon, 16 Jun 2025 23:10:50 +0800 Subject: [PATCH] refactor: shortcut service --- proto/api/v1/shortcut_service.proto | 103 ++++-- proto/gen/api/v1/shortcut_service.pb.go | 265 ++++++++++----- proto/gen/api/v1/shortcut_service.pb.gw.go | 168 ++++++--- proto/gen/api/v1/shortcut_service_grpc.pb.go | 40 +++ proto/gen/apidocs.swagger.yaml | 213 +++++++----- server/router/api/v1/shortcuts_service.go | 102 +++++- web/src/components/CreateShortcutDialog.tsx | 16 +- .../HomeSidebar/ShortcutsSection.tsx | 18 +- web/src/pages/Home.tsx | 9 +- .../types/proto/api/v1/shortcut_service.ts | 321 +++++++++++++----- 10 files changed, 895 insertions(+), 360 deletions(-) diff --git a/proto/api/v1/shortcut_service.proto b/proto/api/v1/shortcut_service.proto index cfdd3a7b0..6f27d4680 100644 --- a/proto/api/v1/shortcut_service.proto +++ b/proto/api/v1/shortcut_service.proto @@ -4,6 +4,8 @@ package memos.api.v1; import "google/api/annotations.proto"; import "google/api/client.proto"; +import "google/api/field_behavior.proto"; +import "google/api/resource.proto"; import "google/protobuf/empty.proto"; import "google/protobuf/field_mask.proto"; @@ -16,6 +18,12 @@ service ShortcutService { option (google.api.method_signature) = "parent"; } + // GetShortcut gets a shortcut by name. + rpc GetShortcut(GetShortcutRequest) returns (Shortcut) { + option (google.api.http) = {get: "/api/v1/{name=users/*/shortcuts/*}"}; + option (google.api.method_signature) = "name"; + } + // CreateShortcut creates a new shortcut for a user. rpc CreateShortcut(CreateShortcutRequest) returns (Shortcut) { option (google.api.http) = { @@ -28,56 +36,101 @@ service ShortcutService { // UpdateShortcut updates a shortcut for a user. rpc UpdateShortcut(UpdateShortcutRequest) returns (Shortcut) { option (google.api.http) = { - patch: "/api/v1/{parent=users/*}/shortcuts/{shortcut.id}" + patch: "/api/v1/{shortcut.name=users/*/shortcuts/*}" body: "shortcut" }; - option (google.api.method_signature) = "parent,shortcut,update_mask"; + option (google.api.method_signature) = "shortcut,update_mask"; } // DeleteShortcut deletes a shortcut for a user. rpc DeleteShortcut(DeleteShortcutRequest) returns (google.protobuf.Empty) { - option (google.api.http) = {delete: "/api/v1/{parent=users/*}/shortcuts/{id}"}; - option (google.api.method_signature) = "parent,id"; + option (google.api.http) = {delete: "/api/v1/{name=users/*/shortcuts/*}"}; + option (google.api.method_signature) = "name"; } } message Shortcut { - string id = 1; - string title = 2; - string filter = 3; + option (google.api.resource) = { + type: "memos.api.v1/Shortcut" + pattern: "users/{user}/shortcuts/{shortcut}" + singular: "shortcut" + plural: "shortcuts" + }; + + // The resource name of the shortcut. + // Format: users/{user}/shortcuts/{shortcut} + string name = 1 [(google.api.field_behavior) = IDENTIFIER]; + + // The title of the shortcut. + string title = 2 [(google.api.field_behavior) = REQUIRED]; + + // The filter expression for the shortcut. + string filter = 3 [(google.api.field_behavior) = OPTIONAL]; } message ListShortcutsRequest { - // The name of the user. - string parent = 1; + // Required. The parent resource where shortcuts are listed. + // Format: users/{user} + string parent = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = {child_type: "memos.api.v1/Shortcut"} + ]; + + // Optional. The maximum number of shortcuts to return. + int32 page_size = 2 [(google.api.field_behavior) = OPTIONAL]; + + // Optional. A page token for pagination. + string page_token = 3 [(google.api.field_behavior) = OPTIONAL]; } message ListShortcutsResponse { + // The list of shortcuts. repeated Shortcut shortcuts = 1; -} -message CreateShortcutRequest { - // The name of the user. - string parent = 1; + // A token for the next page of results. + string next_page_token = 2; + + // The total count of shortcuts. + int32 total_size = 3; +} - Shortcut shortcut = 2; +message GetShortcutRequest { + // Required. The resource name of the shortcut to retrieve. + // Format: users/{user}/shortcuts/{shortcut} + string name = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = {type: "memos.api.v1/Shortcut"} + ]; +} - bool validate_only = 3; +message CreateShortcutRequest { + // Required. The parent resource where this shortcut will be created. + // Format: users/{user} + string parent = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = {child_type: "memos.api.v1/Shortcut"} + ]; + + // Required. The shortcut to create. + Shortcut shortcut = 2 [(google.api.field_behavior) = REQUIRED]; + + // Optional. If set, validate the request, but do not actually create the shortcut. + bool validate_only = 3 [(google.api.field_behavior) = OPTIONAL]; } message UpdateShortcutRequest { - // The name of the user. - string parent = 1; + // Required. The shortcut resource which replaces the resource on the server. + Shortcut shortcut = 1 [(google.api.field_behavior) = REQUIRED]; - Shortcut shortcut = 2; - - google.protobuf.FieldMask update_mask = 3; + // Optional. The list of fields to update. + google.protobuf.FieldMask update_mask = 2 [(google.api.field_behavior) = OPTIONAL]; } message DeleteShortcutRequest { - // The name of the user. - string parent = 1; - - // The id of the shortcut. - string id = 2; + // Required. The resource name of the shortcut to delete. + // Format: users/{user}/shortcuts/{shortcut} + string name = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = {type: "memos.api.v1/Shortcut"} + ]; } diff --git a/proto/gen/api/v1/shortcut_service.pb.go b/proto/gen/api/v1/shortcut_service.pb.go index bc53bc6ff..0149be9d5 100644 --- a/proto/gen/api/v1/shortcut_service.pb.go +++ b/proto/gen/api/v1/shortcut_service.pb.go @@ -25,10 +25,14 @@ const ( ) type Shortcut struct { - state protoimpl.MessageState `protogen:"open.v1"` - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - Title string `protobuf:"bytes,2,opt,name=title,proto3" json:"title,omitempty"` - Filter string `protobuf:"bytes,3,opt,name=filter,proto3" json:"filter,omitempty"` + state protoimpl.MessageState `protogen:"open.v1"` + // The resource name of the shortcut. + // Format: users/{user}/shortcuts/{shortcut} + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + // The title of the shortcut. + Title string `protobuf:"bytes,2,opt,name=title,proto3" json:"title,omitempty"` + // The filter expression for the shortcut. + Filter string `protobuf:"bytes,3,opt,name=filter,proto3" json:"filter,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -63,9 +67,9 @@ func (*Shortcut) Descriptor() ([]byte, []int) { return file_api_v1_shortcut_service_proto_rawDescGZIP(), []int{0} } -func (x *Shortcut) GetId() string { +func (x *Shortcut) GetName() string { if x != nil { - return x.Id + return x.Name } return "" } @@ -86,8 +90,13 @@ func (x *Shortcut) GetFilter() string { type ListShortcutsRequest struct { state protoimpl.MessageState `protogen:"open.v1"` - // The name of the user. - Parent string `protobuf:"bytes,1,opt,name=parent,proto3" json:"parent,omitempty"` + // Required. The parent resource where shortcuts are listed. + // Format: users/{user} + Parent string `protobuf:"bytes,1,opt,name=parent,proto3" json:"parent,omitempty"` + // Optional. The maximum number of shortcuts to return. + PageSize int32 `protobuf:"varint,2,opt,name=page_size,json=pageSize,proto3" json:"page_size,omitempty"` + // Optional. A page token for pagination. + PageToken string `protobuf:"bytes,3,opt,name=page_token,json=pageToken,proto3" json:"page_token,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -129,9 +138,28 @@ func (x *ListShortcutsRequest) GetParent() string { return "" } +func (x *ListShortcutsRequest) GetPageSize() int32 { + if x != nil { + return x.PageSize + } + return 0 +} + +func (x *ListShortcutsRequest) GetPageToken() string { + if x != nil { + return x.PageToken + } + return "" +} + type ListShortcutsResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - Shortcuts []*Shortcut `protobuf:"bytes,1,rep,name=shortcuts,proto3" json:"shortcuts,omitempty"` + state protoimpl.MessageState `protogen:"open.v1"` + // The list of shortcuts. + Shortcuts []*Shortcut `protobuf:"bytes,1,rep,name=shortcuts,proto3" json:"shortcuts,omitempty"` + // A token for the next page of results. + NextPageToken string `protobuf:"bytes,2,opt,name=next_page_token,json=nextPageToken,proto3" json:"next_page_token,omitempty"` + // The total count of shortcuts. + TotalSize int32 `protobuf:"varint,3,opt,name=total_size,json=totalSize,proto3" json:"total_size,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -173,19 +201,82 @@ func (x *ListShortcutsResponse) GetShortcuts() []*Shortcut { return nil } +func (x *ListShortcutsResponse) GetNextPageToken() string { + if x != nil { + return x.NextPageToken + } + return "" +} + +func (x *ListShortcutsResponse) GetTotalSize() int32 { + if x != nil { + return x.TotalSize + } + return 0 +} + +type GetShortcutRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Required. The resource name of the shortcut to retrieve. + // Format: users/{user}/shortcuts/{shortcut} + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetShortcutRequest) Reset() { + *x = GetShortcutRequest{} + mi := &file_api_v1_shortcut_service_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetShortcutRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetShortcutRequest) ProtoMessage() {} + +func (x *GetShortcutRequest) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_shortcut_service_proto_msgTypes[3] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetShortcutRequest.ProtoReflect.Descriptor instead. +func (*GetShortcutRequest) Descriptor() ([]byte, []int) { + return file_api_v1_shortcut_service_proto_rawDescGZIP(), []int{3} +} + +func (x *GetShortcutRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + type CreateShortcutRequest struct { state protoimpl.MessageState `protogen:"open.v1"` - // The name of the user. - Parent string `protobuf:"bytes,1,opt,name=parent,proto3" json:"parent,omitempty"` - Shortcut *Shortcut `protobuf:"bytes,2,opt,name=shortcut,proto3" json:"shortcut,omitempty"` - ValidateOnly bool `protobuf:"varint,3,opt,name=validate_only,json=validateOnly,proto3" json:"validate_only,omitempty"` + // Required. The parent resource where this shortcut will be created. + // Format: users/{user} + Parent string `protobuf:"bytes,1,opt,name=parent,proto3" json:"parent,omitempty"` + // Required. The shortcut to create. + Shortcut *Shortcut `protobuf:"bytes,2,opt,name=shortcut,proto3" json:"shortcut,omitempty"` + // Optional. If set, validate the request, but do not actually create the shortcut. + ValidateOnly bool `protobuf:"varint,3,opt,name=validate_only,json=validateOnly,proto3" json:"validate_only,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *CreateShortcutRequest) Reset() { *x = CreateShortcutRequest{} - mi := &file_api_v1_shortcut_service_proto_msgTypes[3] + mi := &file_api_v1_shortcut_service_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -197,7 +288,7 @@ func (x *CreateShortcutRequest) String() string { func (*CreateShortcutRequest) ProtoMessage() {} func (x *CreateShortcutRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_shortcut_service_proto_msgTypes[3] + mi := &file_api_v1_shortcut_service_proto_msgTypes[4] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -210,7 +301,7 @@ func (x *CreateShortcutRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use CreateShortcutRequest.ProtoReflect.Descriptor instead. func (*CreateShortcutRequest) Descriptor() ([]byte, []int) { - return file_api_v1_shortcut_service_proto_rawDescGZIP(), []int{3} + return file_api_v1_shortcut_service_proto_rawDescGZIP(), []int{4} } func (x *CreateShortcutRequest) GetParent() string { @@ -236,17 +327,17 @@ func (x *CreateShortcutRequest) GetValidateOnly() bool { type UpdateShortcutRequest struct { state protoimpl.MessageState `protogen:"open.v1"` - // The name of the user. - Parent string `protobuf:"bytes,1,opt,name=parent,proto3" json:"parent,omitempty"` - Shortcut *Shortcut `protobuf:"bytes,2,opt,name=shortcut,proto3" json:"shortcut,omitempty"` - UpdateMask *fieldmaskpb.FieldMask `protobuf:"bytes,3,opt,name=update_mask,json=updateMask,proto3" json:"update_mask,omitempty"` + // Required. The shortcut resource which replaces the resource on the server. + Shortcut *Shortcut `protobuf:"bytes,1,opt,name=shortcut,proto3" json:"shortcut,omitempty"` + // Optional. The list of fields to update. + UpdateMask *fieldmaskpb.FieldMask `protobuf:"bytes,2,opt,name=update_mask,json=updateMask,proto3" json:"update_mask,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *UpdateShortcutRequest) Reset() { *x = UpdateShortcutRequest{} - mi := &file_api_v1_shortcut_service_proto_msgTypes[4] + mi := &file_api_v1_shortcut_service_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -258,7 +349,7 @@ func (x *UpdateShortcutRequest) String() string { func (*UpdateShortcutRequest) ProtoMessage() {} func (x *UpdateShortcutRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_shortcut_service_proto_msgTypes[4] + mi := &file_api_v1_shortcut_service_proto_msgTypes[5] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -271,14 +362,7 @@ func (x *UpdateShortcutRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use UpdateShortcutRequest.ProtoReflect.Descriptor instead. func (*UpdateShortcutRequest) Descriptor() ([]byte, []int) { - return file_api_v1_shortcut_service_proto_rawDescGZIP(), []int{4} -} - -func (x *UpdateShortcutRequest) GetParent() string { - if x != nil { - return x.Parent - } - return "" + return file_api_v1_shortcut_service_proto_rawDescGZIP(), []int{5} } func (x *UpdateShortcutRequest) GetShortcut() *Shortcut { @@ -297,17 +381,16 @@ func (x *UpdateShortcutRequest) GetUpdateMask() *fieldmaskpb.FieldMask { type DeleteShortcutRequest struct { state protoimpl.MessageState `protogen:"open.v1"` - // The name of the user. - Parent string `protobuf:"bytes,1,opt,name=parent,proto3" json:"parent,omitempty"` - // The id of the shortcut. - Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"` + // Required. The resource name of the shortcut to delete. + // Format: users/{user}/shortcuts/{shortcut} + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *DeleteShortcutRequest) Reset() { *x = DeleteShortcutRequest{} - mi := &file_api_v1_shortcut_service_proto_msgTypes[5] + mi := &file_api_v1_shortcut_service_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -319,7 +402,7 @@ func (x *DeleteShortcutRequest) String() string { func (*DeleteShortcutRequest) ProtoMessage() {} func (x *DeleteShortcutRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_shortcut_service_proto_msgTypes[5] + mi := &file_api_v1_shortcut_service_proto_msgTypes[6] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -332,19 +415,12 @@ func (x *DeleteShortcutRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteShortcutRequest.ProtoReflect.Descriptor instead. func (*DeleteShortcutRequest) Descriptor() ([]byte, []int) { - return file_api_v1_shortcut_service_proto_rawDescGZIP(), []int{5} -} - -func (x *DeleteShortcutRequest) GetParent() string { - if x != nil { - return x.Parent - } - return "" + return file_api_v1_shortcut_service_proto_rawDescGZIP(), []int{6} } -func (x *DeleteShortcutRequest) GetId() string { +func (x *DeleteShortcutRequest) GetName() string { if x != nil { - return x.Id + return x.Name } return "" } @@ -353,32 +429,42 @@ var File_api_v1_shortcut_service_proto protoreflect.FileDescriptor const file_api_v1_shortcut_service_proto_rawDesc = "" + "\n" + - "\x1dapi/v1/shortcut_service.proto\x12\fmemos.api.v1\x1a\x1cgoogle/api/annotations.proto\x1a\x17google/api/client.proto\x1a\x1bgoogle/protobuf/empty.proto\x1a google/protobuf/field_mask.proto\"H\n" + - "\bShortcut\x12\x0e\n" + - "\x02id\x18\x01 \x01(\tR\x02id\x12\x14\n" + - "\x05title\x18\x02 \x01(\tR\x05title\x12\x16\n" + - "\x06filter\x18\x03 \x01(\tR\x06filter\".\n" + - "\x14ListShortcutsRequest\x12\x16\n" + - "\x06parent\x18\x01 \x01(\tR\x06parent\"M\n" + + "\x1dapi/v1/shortcut_service.proto\x12\fmemos.api.v1\x1a\x1cgoogle/api/annotations.proto\x1a\x17google/api/client.proto\x1a\x1fgoogle/api/field_behavior.proto\x1a\x19google/api/resource.proto\x1a\x1bgoogle/protobuf/empty.proto\x1a google/protobuf/field_mask.proto\"\xaf\x01\n" + + "\bShortcut\x12\x17\n" + + "\x04name\x18\x01 \x01(\tB\x03\xe0A\bR\x04name\x12\x19\n" + + "\x05title\x18\x02 \x01(\tB\x03\xe0A\x02R\x05title\x12\x1b\n" + + "\x06filter\x18\x03 \x01(\tB\x03\xe0A\x01R\x06filter:R\xeaAO\n" + + "\x15memos.api.v1/Shortcut\x12!users/{user}/shortcuts/{shortcut}*\tshortcuts2\bshortcut\"\x93\x01\n" + + "\x14ListShortcutsRequest\x125\n" + + "\x06parent\x18\x01 \x01(\tB\x1d\xe0A\x02\xfaA\x17\x12\x15memos.api.v1/ShortcutR\x06parent\x12 \n" + + "\tpage_size\x18\x02 \x01(\x05B\x03\xe0A\x01R\bpageSize\x12\"\n" + + "\n" + + "page_token\x18\x03 \x01(\tB\x03\xe0A\x01R\tpageToken\"\x94\x01\n" + "\x15ListShortcutsResponse\x124\n" + - "\tshortcuts\x18\x01 \x03(\v2\x16.memos.api.v1.ShortcutR\tshortcuts\"\x88\x01\n" + - "\x15CreateShortcutRequest\x12\x16\n" + - "\x06parent\x18\x01 \x01(\tR\x06parent\x122\n" + - "\bshortcut\x18\x02 \x01(\v2\x16.memos.api.v1.ShortcutR\bshortcut\x12#\n" + - "\rvalidate_only\x18\x03 \x01(\bR\fvalidateOnly\"\xa0\x01\n" + - "\x15UpdateShortcutRequest\x12\x16\n" + - "\x06parent\x18\x01 \x01(\tR\x06parent\x122\n" + - "\bshortcut\x18\x02 \x01(\v2\x16.memos.api.v1.ShortcutR\bshortcut\x12;\n" + - "\vupdate_mask\x18\x03 \x01(\v2\x1a.google.protobuf.FieldMaskR\n" + - "updateMask\"?\n" + - "\x15DeleteShortcutRequest\x12\x16\n" + - "\x06parent\x18\x01 \x01(\tR\x06parent\x12\x0e\n" + - "\x02id\x18\x02 \x01(\tR\x02id2\xf8\x04\n" + + "\tshortcuts\x18\x01 \x03(\v2\x16.memos.api.v1.ShortcutR\tshortcuts\x12&\n" + + "\x0fnext_page_token\x18\x02 \x01(\tR\rnextPageToken\x12\x1d\n" + + "\n" + + "total_size\x18\x03 \x01(\x05R\ttotalSize\"G\n" + + "\x12GetShortcutRequest\x121\n" + + "\x04name\x18\x01 \x01(\tB\x1d\xe0A\x02\xfaA\x17\n" + + "\x15memos.api.v1/ShortcutR\x04name\"\xb1\x01\n" + + "\x15CreateShortcutRequest\x125\n" + + "\x06parent\x18\x01 \x01(\tB\x1d\xe0A\x02\xfaA\x17\x12\x15memos.api.v1/ShortcutR\x06parent\x127\n" + + "\bshortcut\x18\x02 \x01(\v2\x16.memos.api.v1.ShortcutB\x03\xe0A\x02R\bshortcut\x12(\n" + + "\rvalidate_only\x18\x03 \x01(\bB\x03\xe0A\x01R\fvalidateOnly\"\x92\x01\n" + + "\x15UpdateShortcutRequest\x127\n" + + "\bshortcut\x18\x01 \x01(\v2\x16.memos.api.v1.ShortcutB\x03\xe0A\x02R\bshortcut\x12@\n" + + "\vupdate_mask\x18\x02 \x01(\v2\x1a.google.protobuf.FieldMaskB\x03\xe0A\x01R\n" + + "updateMask\"J\n" + + "\x15DeleteShortcutRequest\x121\n" + + "\x04name\x18\x01 \x01(\tB\x1d\xe0A\x02\xfaA\x17\n" + + "\x15memos.api.v1/ShortcutR\x04name2\xde\x05\n" + "\x0fShortcutService\x12\x8d\x01\n" + - "\rListShortcuts\x12\".memos.api.v1.ListShortcutsRequest\x1a#.memos.api.v1.ListShortcutsResponse\"3\xdaA\x06parent\x82\xd3\xe4\x93\x02$\x12\"/api/v1/{parent=users/*}/shortcuts\x12\x95\x01\n" + - "\x0eCreateShortcut\x12#.memos.api.v1.CreateShortcutRequest\x1a\x16.memos.api.v1.Shortcut\"F\xdaA\x0fparent,shortcut\x82\xd3\xe4\x93\x02.:\bshortcut\"\"/api/v1/{parent=users/*}/shortcuts\x12\xaf\x01\n" + - "\x0eUpdateShortcut\x12#.memos.api.v1.UpdateShortcutRequest\x1a\x16.memos.api.v1.Shortcut\"`\xdaA\x1bparent,shortcut,update_mask\x82\xd3\xe4\x93\x02<:\bshortcut20/api/v1/{parent=users/*}/shortcuts/{shortcut.id}\x12\x8a\x01\n" + - "\x0eDeleteShortcut\x12#.memos.api.v1.DeleteShortcutRequest\x1a\x16.google.protobuf.Empty\";\xdaA\tparent,id\x82\xd3\xe4\x93\x02)*'/api/v1/{parent=users/*}/shortcuts/{id}B\xac\x01\n" + + "\rListShortcuts\x12\".memos.api.v1.ListShortcutsRequest\x1a#.memos.api.v1.ListShortcutsResponse\"3\xdaA\x06parent\x82\xd3\xe4\x93\x02$\x12\"/api/v1/{parent=users/*}/shortcuts\x12z\n" + + "\vGetShortcut\x12 .memos.api.v1.GetShortcutRequest\x1a\x16.memos.api.v1.Shortcut\"1\xdaA\x04name\x82\xd3\xe4\x93\x02$\x12\"/api/v1/{name=users/*/shortcuts/*}\x12\x95\x01\n" + + "\x0eCreateShortcut\x12#.memos.api.v1.CreateShortcutRequest\x1a\x16.memos.api.v1.Shortcut\"F\xdaA\x0fparent,shortcut\x82\xd3\xe4\x93\x02.:\bshortcut\"\"/api/v1/{parent=users/*}/shortcuts\x12\xa3\x01\n" + + "\x0eUpdateShortcut\x12#.memos.api.v1.UpdateShortcutRequest\x1a\x16.memos.api.v1.Shortcut\"T\xdaA\x14shortcut,update_mask\x82\xd3\xe4\x93\x027:\bshortcut2+/api/v1/{shortcut.name=users/*/shortcuts/*}\x12\x80\x01\n" + + "\x0eDeleteShortcut\x12#.memos.api.v1.DeleteShortcutRequest\x1a\x16.google.protobuf.Empty\"1\xdaA\x04name\x82\xd3\xe4\x93\x02$*\"/api/v1/{name=users/*/shortcuts/*}B\xac\x01\n" + "\x10com.memos.api.v1B\x14ShortcutServiceProtoP\x01Z0github.com/usememos/memos/proto/gen/api/v1;apiv1\xa2\x02\x03MAX\xaa\x02\fMemos.Api.V1\xca\x02\fMemos\\Api\\V1\xe2\x02\x18Memos\\Api\\V1\\GPBMetadata\xea\x02\x0eMemos::Api::V1b\x06proto3" var ( @@ -393,32 +479,35 @@ func file_api_v1_shortcut_service_proto_rawDescGZIP() []byte { return file_api_v1_shortcut_service_proto_rawDescData } -var file_api_v1_shortcut_service_proto_msgTypes = make([]protoimpl.MessageInfo, 6) +var file_api_v1_shortcut_service_proto_msgTypes = make([]protoimpl.MessageInfo, 7) var file_api_v1_shortcut_service_proto_goTypes = []any{ (*Shortcut)(nil), // 0: memos.api.v1.Shortcut (*ListShortcutsRequest)(nil), // 1: memos.api.v1.ListShortcutsRequest (*ListShortcutsResponse)(nil), // 2: memos.api.v1.ListShortcutsResponse - (*CreateShortcutRequest)(nil), // 3: memos.api.v1.CreateShortcutRequest - (*UpdateShortcutRequest)(nil), // 4: memos.api.v1.UpdateShortcutRequest - (*DeleteShortcutRequest)(nil), // 5: memos.api.v1.DeleteShortcutRequest - (*fieldmaskpb.FieldMask)(nil), // 6: google.protobuf.FieldMask - (*emptypb.Empty)(nil), // 7: google.protobuf.Empty + (*GetShortcutRequest)(nil), // 3: memos.api.v1.GetShortcutRequest + (*CreateShortcutRequest)(nil), // 4: memos.api.v1.CreateShortcutRequest + (*UpdateShortcutRequest)(nil), // 5: memos.api.v1.UpdateShortcutRequest + (*DeleteShortcutRequest)(nil), // 6: memos.api.v1.DeleteShortcutRequest + (*fieldmaskpb.FieldMask)(nil), // 7: google.protobuf.FieldMask + (*emptypb.Empty)(nil), // 8: google.protobuf.Empty } var file_api_v1_shortcut_service_proto_depIdxs = []int32{ 0, // 0: memos.api.v1.ListShortcutsResponse.shortcuts:type_name -> memos.api.v1.Shortcut 0, // 1: memos.api.v1.CreateShortcutRequest.shortcut:type_name -> memos.api.v1.Shortcut 0, // 2: memos.api.v1.UpdateShortcutRequest.shortcut:type_name -> memos.api.v1.Shortcut - 6, // 3: memos.api.v1.UpdateShortcutRequest.update_mask:type_name -> google.protobuf.FieldMask + 7, // 3: memos.api.v1.UpdateShortcutRequest.update_mask:type_name -> google.protobuf.FieldMask 1, // 4: memos.api.v1.ShortcutService.ListShortcuts:input_type -> memos.api.v1.ListShortcutsRequest - 3, // 5: memos.api.v1.ShortcutService.CreateShortcut:input_type -> memos.api.v1.CreateShortcutRequest - 4, // 6: memos.api.v1.ShortcutService.UpdateShortcut:input_type -> memos.api.v1.UpdateShortcutRequest - 5, // 7: memos.api.v1.ShortcutService.DeleteShortcut:input_type -> memos.api.v1.DeleteShortcutRequest - 2, // 8: memos.api.v1.ShortcutService.ListShortcuts:output_type -> memos.api.v1.ListShortcutsResponse - 0, // 9: memos.api.v1.ShortcutService.CreateShortcut:output_type -> memos.api.v1.Shortcut - 0, // 10: memos.api.v1.ShortcutService.UpdateShortcut:output_type -> memos.api.v1.Shortcut - 7, // 11: memos.api.v1.ShortcutService.DeleteShortcut:output_type -> google.protobuf.Empty - 8, // [8:12] is the sub-list for method output_type - 4, // [4:8] is the sub-list for method input_type + 3, // 5: memos.api.v1.ShortcutService.GetShortcut:input_type -> memos.api.v1.GetShortcutRequest + 4, // 6: memos.api.v1.ShortcutService.CreateShortcut:input_type -> memos.api.v1.CreateShortcutRequest + 5, // 7: memos.api.v1.ShortcutService.UpdateShortcut:input_type -> memos.api.v1.UpdateShortcutRequest + 6, // 8: memos.api.v1.ShortcutService.DeleteShortcut:input_type -> memos.api.v1.DeleteShortcutRequest + 2, // 9: memos.api.v1.ShortcutService.ListShortcuts:output_type -> memos.api.v1.ListShortcutsResponse + 0, // 10: memos.api.v1.ShortcutService.GetShortcut:output_type -> memos.api.v1.Shortcut + 0, // 11: memos.api.v1.ShortcutService.CreateShortcut:output_type -> memos.api.v1.Shortcut + 0, // 12: memos.api.v1.ShortcutService.UpdateShortcut:output_type -> memos.api.v1.Shortcut + 8, // 13: memos.api.v1.ShortcutService.DeleteShortcut:output_type -> google.protobuf.Empty + 9, // [9:14] is the sub-list for method output_type + 4, // [4:9] is the sub-list for method input_type 4, // [4:4] is the sub-list for extension type_name 4, // [4:4] is the sub-list for extension extendee 0, // [0:4] is the sub-list for field type_name @@ -435,7 +524,7 @@ func file_api_v1_shortcut_service_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_api_v1_shortcut_service_proto_rawDesc), len(file_api_v1_shortcut_service_proto_rawDesc)), NumEnums: 0, - NumMessages: 6, + NumMessages: 7, NumExtensions: 0, NumServices: 1, }, diff --git a/proto/gen/api/v1/shortcut_service.pb.gw.go b/proto/gen/api/v1/shortcut_service.pb.gw.go index 0ff147610..2afb900a1 100644 --- a/proto/gen/api/v1/shortcut_service.pb.gw.go +++ b/proto/gen/api/v1/shortcut_service.pb.gw.go @@ -35,6 +35,8 @@ var ( _ = metadata.Join ) +var filter_ShortcutService_ListShortcuts_0 = &utilities.DoubleArray{Encoding: map[string]int{"parent": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}} + func request_ShortcutService_ListShortcuts_0(ctx context.Context, marshaler runtime.Marshaler, client ShortcutServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var ( protoReq ListShortcutsRequest @@ -50,6 +52,12 @@ func request_ShortcutService_ListShortcuts_0(ctx context.Context, marshaler runt 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_ShortcutService_ListShortcuts_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } msg, err := client.ListShortcuts(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err } @@ -68,10 +76,53 @@ func local_request_ShortcutService_ListShortcuts_0(ctx context.Context, marshale 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_ShortcutService_ListShortcuts_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } msg, err := server.ListShortcuts(ctx, &protoReq) return msg, metadata, err } +func request_ShortcutService_GetShortcut_0(ctx context.Context, marshaler runtime.Marshaler, client ShortcutServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var ( + protoReq GetShortcutRequest + metadata runtime.ServerMetadata + err error + ) + io.Copy(io.Discard, req.Body) + val, ok := pathParams["name"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "name") + } + protoReq.Name, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "name", err) + } + msg, err := client.GetShortcut(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err +} + +func local_request_ShortcutService_GetShortcut_0(ctx context.Context, marshaler runtime.Marshaler, server ShortcutServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var ( + protoReq GetShortcutRequest + metadata runtime.ServerMetadata + err error + ) + val, ok := pathParams["name"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "name") + } + protoReq.Name, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "name", err) + } + msg, err := server.GetShortcut(ctx, &protoReq) + return msg, metadata, err +} + var filter_ShortcutService_CreateShortcut_0 = &utilities.DoubleArray{Encoding: map[string]int{"shortcut": 0, "parent": 1}, Base: []int{1, 1, 2, 0, 0}, Check: []int{0, 1, 1, 2, 3}} func request_ShortcutService_CreateShortcut_0(ctx context.Context, marshaler runtime.Marshaler, client ShortcutServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { @@ -128,7 +179,7 @@ func local_request_ShortcutService_CreateShortcut_0(ctx context.Context, marshal return msg, metadata, err } -var filter_ShortcutService_UpdateShortcut_0 = &utilities.DoubleArray{Encoding: map[string]int{"shortcut": 0, "parent": 1, "id": 2}, Base: []int{1, 2, 3, 1, 0, 0, 0}, Check: []int{0, 1, 1, 2, 4, 2, 3}} +var filter_ShortcutService_UpdateShortcut_0 = &utilities.DoubleArray{Encoding: map[string]int{"shortcut": 0, "name": 1}, Base: []int{1, 2, 1, 0, 0}, Check: []int{0, 1, 2, 3, 2}} func request_ShortcutService_UpdateShortcut_0(ctx context.Context, marshaler runtime.Marshaler, client ShortcutServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var ( @@ -150,21 +201,13 @@ func request_ShortcutService_UpdateShortcut_0(ctx context.Context, marshaler run protoReq.UpdateMask = fieldMask } } - 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) - } - val, ok = pathParams["shortcut.id"] + val, ok := pathParams["shortcut.name"] if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "shortcut.id") + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "shortcut.name") } - err = runtime.PopulateFieldFromPath(&protoReq, "shortcut.id", val) + err = runtime.PopulateFieldFromPath(&protoReq, "shortcut.name", val) if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "shortcut.id", err) + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "shortcut.name", err) } if err := req.ParseForm(); err != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) @@ -196,21 +239,13 @@ func local_request_ShortcutService_UpdateShortcut_0(ctx context.Context, marshal protoReq.UpdateMask = fieldMask } } - val, ok := pathParams["parent"] + val, ok := pathParams["shortcut.name"] if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "parent") + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "shortcut.name") } - protoReq.Parent, err = runtime.String(val) + err = runtime.PopulateFieldFromPath(&protoReq, "shortcut.name", val) if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "parent", err) - } - val, ok = pathParams["shortcut.id"] - if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "shortcut.id") - } - err = runtime.PopulateFieldFromPath(&protoReq, "shortcut.id", val) - if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "shortcut.id", err) + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "shortcut.name", err) } if err := req.ParseForm(); err != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) @@ -229,21 +264,13 @@ func request_ShortcutService_DeleteShortcut_0(ctx context.Context, marshaler run err error ) io.Copy(io.Discard, req.Body) - 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) - } - val, ok = pathParams["id"] + val, ok := pathParams["name"] if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id") + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "name") } - protoReq.Id, err = runtime.String(val) + protoReq.Name, err = runtime.String(val) if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err) + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "name", err) } msg, err := client.DeleteShortcut(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err @@ -255,21 +282,13 @@ func local_request_ShortcutService_DeleteShortcut_0(ctx context.Context, marshal 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) - } - val, ok = pathParams["id"] + val, ok := pathParams["name"] if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id") + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "name") } - protoReq.Id, err = runtime.String(val) + protoReq.Name, err = runtime.String(val) if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err) + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "name", err) } msg, err := server.DeleteShortcut(ctx, &protoReq) return msg, metadata, err @@ -301,6 +320,26 @@ func RegisterShortcutServiceHandlerServer(ctx context.Context, mux *runtime.Serv } forward_ShortcutService_ListShortcuts_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) + mux.Handle(http.MethodGet, pattern_ShortcutService_GetShortcut_0, 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.ShortcutService/GetShortcut", runtime.WithHTTPPathPattern("/api/v1/{name=users/*/shortcuts/*}")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_ShortcutService_GetShortcut_0(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_ShortcutService_GetShortcut_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + }) mux.Handle(http.MethodPost, pattern_ShortcutService_CreateShortcut_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() @@ -327,7 +366,7 @@ func RegisterShortcutServiceHandlerServer(ctx context.Context, mux *runtime.Serv 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.ShortcutService/UpdateShortcut", runtime.WithHTTPPathPattern("/api/v1/{parent=users/*}/shortcuts/{shortcut.id}")) + annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v1.ShortcutService/UpdateShortcut", runtime.WithHTTPPathPattern("/api/v1/{shortcut.name=users/*/shortcuts/*}")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return @@ -347,7 +386,7 @@ func RegisterShortcutServiceHandlerServer(ctx context.Context, mux *runtime.Serv 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.ShortcutService/DeleteShortcut", runtime.WithHTTPPathPattern("/api/v1/{parent=users/*}/shortcuts/{id}")) + annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v1.ShortcutService/DeleteShortcut", runtime.WithHTTPPathPattern("/api/v1/{name=users/*/shortcuts/*}")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return @@ -418,6 +457,23 @@ func RegisterShortcutServiceHandlerClient(ctx context.Context, mux *runtime.Serv } forward_ShortcutService_ListShortcuts_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) + mux.Handle(http.MethodGet, pattern_ShortcutService_GetShortcut_0, 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.ShortcutService/GetShortcut", runtime.WithHTTPPathPattern("/api/v1/{name=users/*/shortcuts/*}")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_ShortcutService_GetShortcut_0(annotatedContext, inboundMarshaler, client, req, pathParams) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + forward_ShortcutService_GetShortcut_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + }) mux.Handle(http.MethodPost, pattern_ShortcutService_CreateShortcut_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() @@ -439,7 +495,7 @@ func RegisterShortcutServiceHandlerClient(ctx context.Context, mux *runtime.Serv ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/memos.api.v1.ShortcutService/UpdateShortcut", runtime.WithHTTPPathPattern("/api/v1/{parent=users/*}/shortcuts/{shortcut.id}")) + annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/memos.api.v1.ShortcutService/UpdateShortcut", runtime.WithHTTPPathPattern("/api/v1/{shortcut.name=users/*/shortcuts/*}")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return @@ -456,7 +512,7 @@ func RegisterShortcutServiceHandlerClient(ctx context.Context, mux *runtime.Serv ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/memos.api.v1.ShortcutService/DeleteShortcut", runtime.WithHTTPPathPattern("/api/v1/{parent=users/*}/shortcuts/{id}")) + annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/memos.api.v1.ShortcutService/DeleteShortcut", runtime.WithHTTPPathPattern("/api/v1/{name=users/*/shortcuts/*}")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return @@ -474,13 +530,15 @@ func RegisterShortcutServiceHandlerClient(ctx context.Context, mux *runtime.Serv var ( pattern_ShortcutService_ListShortcuts_0 = 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", "shortcuts"}, "")) + pattern_ShortcutService_GetShortcut_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 2, 3, 1, 0, 4, 4, 5, 4}, []string{"api", "v1", "users", "shortcuts", "name"}, "")) pattern_ShortcutService_CreateShortcut_0 = 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", "shortcuts"}, "")) - pattern_ShortcutService_UpdateShortcut_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4, 1, 0, 4, 1, 5, 5}, []string{"api", "v1", "users", "parent", "shortcuts", "shortcut.id"}, "")) - pattern_ShortcutService_DeleteShortcut_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4, 1, 0, 4, 1, 5, 5}, []string{"api", "v1", "users", "parent", "shortcuts", "id"}, "")) + pattern_ShortcutService_UpdateShortcut_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 2, 3, 1, 0, 4, 4, 5, 4}, []string{"api", "v1", "users", "shortcuts", "shortcut.name"}, "")) + pattern_ShortcutService_DeleteShortcut_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 2, 3, 1, 0, 4, 4, 5, 4}, []string{"api", "v1", "users", "shortcuts", "name"}, "")) ) var ( forward_ShortcutService_ListShortcuts_0 = runtime.ForwardResponseMessage + forward_ShortcutService_GetShortcut_0 = runtime.ForwardResponseMessage forward_ShortcutService_CreateShortcut_0 = runtime.ForwardResponseMessage forward_ShortcutService_UpdateShortcut_0 = runtime.ForwardResponseMessage forward_ShortcutService_DeleteShortcut_0 = runtime.ForwardResponseMessage diff --git a/proto/gen/api/v1/shortcut_service_grpc.pb.go b/proto/gen/api/v1/shortcut_service_grpc.pb.go index a9b61572b..3a3f5b89e 100644 --- a/proto/gen/api/v1/shortcut_service_grpc.pb.go +++ b/proto/gen/api/v1/shortcut_service_grpc.pb.go @@ -21,6 +21,7 @@ const _ = grpc.SupportPackageIsVersion9 const ( ShortcutService_ListShortcuts_FullMethodName = "/memos.api.v1.ShortcutService/ListShortcuts" + ShortcutService_GetShortcut_FullMethodName = "/memos.api.v1.ShortcutService/GetShortcut" ShortcutService_CreateShortcut_FullMethodName = "/memos.api.v1.ShortcutService/CreateShortcut" ShortcutService_UpdateShortcut_FullMethodName = "/memos.api.v1.ShortcutService/UpdateShortcut" ShortcutService_DeleteShortcut_FullMethodName = "/memos.api.v1.ShortcutService/DeleteShortcut" @@ -32,6 +33,8 @@ const ( type ShortcutServiceClient interface { // ListShortcuts returns a list of shortcuts for a user. ListShortcuts(ctx context.Context, in *ListShortcutsRequest, opts ...grpc.CallOption) (*ListShortcutsResponse, error) + // GetShortcut gets a shortcut by name. + GetShortcut(ctx context.Context, in *GetShortcutRequest, opts ...grpc.CallOption) (*Shortcut, error) // CreateShortcut creates a new shortcut for a user. CreateShortcut(ctx context.Context, in *CreateShortcutRequest, opts ...grpc.CallOption) (*Shortcut, error) // UpdateShortcut updates a shortcut for a user. @@ -58,6 +61,16 @@ func (c *shortcutServiceClient) ListShortcuts(ctx context.Context, in *ListShort return out, nil } +func (c *shortcutServiceClient) GetShortcut(ctx context.Context, in *GetShortcutRequest, opts ...grpc.CallOption) (*Shortcut, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(Shortcut) + err := c.cc.Invoke(ctx, ShortcutService_GetShortcut_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *shortcutServiceClient) CreateShortcut(ctx context.Context, in *CreateShortcutRequest, opts ...grpc.CallOption) (*Shortcut, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(Shortcut) @@ -94,6 +107,8 @@ func (c *shortcutServiceClient) DeleteShortcut(ctx context.Context, in *DeleteSh type ShortcutServiceServer interface { // ListShortcuts returns a list of shortcuts for a user. ListShortcuts(context.Context, *ListShortcutsRequest) (*ListShortcutsResponse, error) + // GetShortcut gets a shortcut by name. + GetShortcut(context.Context, *GetShortcutRequest) (*Shortcut, error) // CreateShortcut creates a new shortcut for a user. CreateShortcut(context.Context, *CreateShortcutRequest) (*Shortcut, error) // UpdateShortcut updates a shortcut for a user. @@ -113,6 +128,9 @@ type UnimplementedShortcutServiceServer struct{} func (UnimplementedShortcutServiceServer) ListShortcuts(context.Context, *ListShortcutsRequest) (*ListShortcutsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method ListShortcuts not implemented") } +func (UnimplementedShortcutServiceServer) GetShortcut(context.Context, *GetShortcutRequest) (*Shortcut, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetShortcut not implemented") +} func (UnimplementedShortcutServiceServer) CreateShortcut(context.Context, *CreateShortcutRequest) (*Shortcut, error) { return nil, status.Errorf(codes.Unimplemented, "method CreateShortcut not implemented") } @@ -161,6 +179,24 @@ func _ShortcutService_ListShortcuts_Handler(srv interface{}, ctx context.Context return interceptor(ctx, in, info, handler) } +func _ShortcutService_GetShortcut_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetShortcutRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ShortcutServiceServer).GetShortcut(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: ShortcutService_GetShortcut_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ShortcutServiceServer).GetShortcut(ctx, req.(*GetShortcutRequest)) + } + return interceptor(ctx, in, info, handler) +} + func _ShortcutService_CreateShortcut_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(CreateShortcutRequest) if err := dec(in); err != nil { @@ -226,6 +262,10 @@ var ShortcutService_ServiceDesc = grpc.ServiceDesc{ MethodName: "ListShortcuts", Handler: _ShortcutService_ListShortcuts_Handler, }, + { + MethodName: "GetShortcut", + Handler: _ShortcutService_GetShortcut_Handler, + }, { MethodName: "CreateShortcut", Handler: _ShortcutService_CreateShortcut_Handler, diff --git a/proto/gen/apidocs.swagger.yaml b/proto/gen/apidocs.swagger.yaml index 1cc0a1086..6ec747f8c 100644 --- a/proto/gen/apidocs.swagger.yaml +++ b/proto/gen/apidocs.swagger.yaml @@ -1083,6 +1083,52 @@ paths: tags: - ResourceService /api/v1/{name_5}: + get: + summary: GetShortcut gets a shortcut by name. + operationId: ShortcutService_GetShortcut + responses: + "200": + description: A successful response. + schema: + $ref: '#/definitions/apiv1Shortcut' + default: + description: An unexpected error response. + schema: + $ref: '#/definitions/googlerpcStatus' + parameters: + - name: name_5 + description: |- + Required. The resource name of the shortcut to retrieve. + Format: users/{user}/shortcuts/{shortcut} + in: path + required: true + type: string + pattern: users/[^/]+/shortcuts/[^/]+ + tags: + - ShortcutService + delete: + summary: DeleteMemo deletes a memo. + operationId: MemoService_DeleteMemo + responses: + "200": + description: A successful response. + schema: + type: object + properties: {} + default: + description: An unexpected error response. + schema: + $ref: '#/definitions/googlerpcStatus' + parameters: + - name: name_5 + description: The name of the memo. + in: path + required: true + type: string + pattern: memos/[^/]+ + tags: + - MemoService + /api/v1/{name_6}: get: summary: GetWebhook gets a webhook by name. operationId: WebhookService_GetWebhook @@ -1096,7 +1142,7 @@ paths: schema: $ref: '#/definitions/googlerpcStatus' parameters: - - name: name_5 + - name: name_6 description: |- Required. The resource name of the webhook. Format: webhooks/{webhook} @@ -1114,8 +1160,8 @@ paths: tags: - WebhookService delete: - summary: DeleteMemo deletes a memo. - operationId: MemoService_DeleteMemo + summary: DeleteShortcut deletes a shortcut for a user. + operationId: ShortcutService_DeleteShortcut responses: "200": description: A successful response. @@ -1127,15 +1173,17 @@ paths: schema: $ref: '#/definitions/googlerpcStatus' parameters: - - name: name_5 - description: The name of the memo. + - name: name_6 + description: |- + Required. The resource name of the shortcut to delete. + Format: users/{user}/shortcuts/{shortcut} in: path required: true type: string - pattern: memos/[^/]+ + pattern: users/[^/]+/shortcuts/[^/]+ tags: - - MemoService - /api/v1/{name_6}: + - ShortcutService + /api/v1/{name_7}: get: summary: Gets a workspace setting. operationId: WorkspaceService_GetWorkspaceSetting @@ -1149,7 +1197,7 @@ paths: schema: $ref: '#/definitions/googlerpcStatus' parameters: - - name: name_6 + - name: name_7 description: |- The resource name of the workspace setting. Format: workspace/settings/{setting} @@ -1173,7 +1221,7 @@ paths: schema: $ref: '#/definitions/googlerpcStatus' parameters: - - name: name_6 + - name: name_7 description: |- Required. The resource name of the webhook to delete. Format: webhooks/{webhook} @@ -1676,11 +1724,24 @@ paths: $ref: '#/definitions/googlerpcStatus' parameters: - name: parent - description: The name of the user. + description: |- + Required. The parent resource where shortcuts are listed. + Format: users/{user} in: path required: true type: string pattern: users/[^/]+ + - name: pageSize + description: Optional. The maximum number of shortcuts to return. + in: query + required: false + type: integer + format: int32 + - name: pageToken + description: Optional. A page token for pagination. + in: query + required: false + type: string tags: - ShortcutService post: @@ -1697,86 +1758,28 @@ paths: $ref: '#/definitions/googlerpcStatus' parameters: - name: parent - description: The name of the user. + description: |- + Required. The parent resource where this shortcut will be created. + Format: users/{user} in: path required: true type: string pattern: users/[^/]+ - name: shortcut + description: Required. The shortcut to create. in: body required: true schema: $ref: '#/definitions/apiv1Shortcut' + required: + - shortcut - name: validateOnly + description: Optional. If set, validate the request, but do not actually create the shortcut. in: query required: false type: boolean tags: - ShortcutService - /api/v1/{parent}/shortcuts/{id}: - delete: - summary: DeleteShortcut deletes a shortcut for a user. - operationId: ShortcutService_DeleteShortcut - responses: - "200": - description: A successful response. - schema: - type: object - properties: {} - default: - description: An unexpected error response. - schema: - $ref: '#/definitions/googlerpcStatus' - parameters: - - name: parent - description: The name of the user. - in: path - required: true - type: string - pattern: users/[^/]+ - - name: id - description: The id of the shortcut. - in: path - required: true - type: string - tags: - - ShortcutService - /api/v1/{parent}/shortcuts/{shortcut.id}: - patch: - summary: UpdateShortcut updates a shortcut for a user. - operationId: ShortcutService_UpdateShortcut - responses: - "200": - description: A successful response. - schema: - $ref: '#/definitions/apiv1Shortcut' - default: - description: An unexpected error response. - schema: - $ref: '#/definitions/googlerpcStatus' - parameters: - - name: parent - description: The name of the user. - in: path - required: true - type: string - pattern: users/[^/]+ - - name: shortcut.id - in: path - required: true - type: string - - name: shortcut - in: body - required: true - schema: - type: object - properties: - title: - type: string - filter: - type: string - tags: - - ShortcutService /api/v1/{parent}/tags/{tag}: delete: summary: DeleteMemoTag deletes a tag for a memo. @@ -1972,6 +1975,47 @@ paths: - setting tags: - UserService + /api/v1/{shortcut.name}: + patch: + summary: UpdateShortcut updates a shortcut for a user. + operationId: ShortcutService_UpdateShortcut + responses: + "200": + description: A successful response. + schema: + $ref: '#/definitions/apiv1Shortcut' + default: + description: An unexpected error response. + schema: + $ref: '#/definitions/googlerpcStatus' + parameters: + - name: shortcut.name + description: |- + The resource name of the shortcut. + Format: users/{user}/shortcuts/{shortcut} + in: path + required: true + type: string + pattern: users/[^/]+/shortcuts/[^/]+ + - name: shortcut + description: Required. The shortcut resource which replaces the resource on the server. + in: body + required: true + schema: + type: object + properties: + title: + type: string + description: The title of the shortcut. + filter: + type: string + description: The filter expression for the shortcut. + title: Required. The shortcut resource which replaces the resource on the server. + required: + - title + - shortcut + tags: + - ShortcutService /api/v1/{user.name}: patch: summary: UpdateUser updates a user. @@ -2479,12 +2523,19 @@ definitions: apiv1Shortcut: type: object properties: - id: + name: type: string + title: |- + The resource name of the shortcut. + Format: users/{user}/shortcuts/{shortcut} title: type: string + description: The title of the shortcut. filter: type: string + description: The filter expression for the shortcut. + required: + - title apiv1UserSetting: type: object properties: @@ -3072,6 +3123,14 @@ definitions: items: type: object $ref: '#/definitions/apiv1Shortcut' + description: The list of shortcuts. + nextPageToken: + type: string + description: A token for the next page of results. + totalSize: + type: integer + format: int32 + description: The total count of shortcuts. v1ListUserAccessTokensResponse: type: object properties: diff --git a/server/router/api/v1/shortcuts_service.go b/server/router/api/v1/shortcuts_service.go index a55a8c2a7..8f3d35dac 100644 --- a/server/router/api/v1/shortcuts_service.go +++ b/server/router/api/v1/shortcuts_service.go @@ -2,6 +2,8 @@ package v1 import ( "context" + "fmt" + "strings" "github.com/pkg/errors" "google.golang.org/grpc/codes" @@ -15,6 +17,32 @@ import ( "github.com/usememos/memos/store" ) +// Helper function to extract user ID and shortcut ID from shortcut resource name. +// Format: users/{user}/shortcuts/{shortcut}. +func extractUserAndShortcutIDFromName(name string) (int32, string, error) { + parts := strings.Split(name, "/") + if len(parts) != 4 || parts[0] != "users" || parts[2] != "shortcuts" { + return 0, "", errors.Errorf("invalid shortcut name format: %s", name) + } + + userID, err := util.ConvertStringToInt32(parts[1]) + if err != nil { + return 0, "", errors.Errorf("invalid user ID %q", parts[1]) + } + + shortcutID := parts[3] + if shortcutID == "" { + return 0, "", errors.Errorf("empty shortcut ID in name: %s", name) + } + + return userID, shortcutID, nil +} + +// Helper function to construct shortcut resource name. +func constructShortcutName(userID int32, shortcutID string) string { + return fmt.Sprintf("users/%d/shortcuts/%s", userID, shortcutID) +} + func (s *APIV1Service) ListShortcuts(ctx context.Context, request *v1pb.ListShortcutsRequest) (*v1pb.ListShortcutsResponse, error) { userID, err := ExtractUserIDFromName(request.Parent) if err != nil { @@ -46,7 +74,7 @@ func (s *APIV1Service) ListShortcuts(ctx context.Context, request *v1pb.ListShor shortcuts := []*v1pb.Shortcut{} for _, shortcut := range shortcutsUserSetting.GetShortcuts() { shortcuts = append(shortcuts, &v1pb.Shortcut{ - Id: shortcut.GetId(), + Name: constructShortcutName(userID, shortcut.GetId()), Title: shortcut.GetTitle(), Filter: shortcut.GetFilter(), }) @@ -57,6 +85,45 @@ func (s *APIV1Service) ListShortcuts(ctx context.Context, request *v1pb.ListShor }, nil } +func (s *APIV1Service) GetShortcut(ctx context.Context, request *v1pb.GetShortcutRequest) (*v1pb.Shortcut, error) { + userID, shortcutID, err := extractUserAndShortcutIDFromName(request.Name) + if err != nil { + return nil, status.Errorf(codes.InvalidArgument, "invalid shortcut name: %v", err) + } + + currentUser, err := s.GetCurrentUser(ctx) + if err != nil { + return nil, status.Errorf(codes.Internal, "failed to get current user: %v", err) + } + if currentUser == nil || currentUser.ID != userID { + return nil, status.Errorf(codes.PermissionDenied, "permission denied") + } + + userSetting, err := s.Store.GetUserSetting(ctx, &store.FindUserSetting{ + UserID: &userID, + Key: storepb.UserSettingKey_SHORTCUTS, + }) + if err != nil { + return nil, err + } + if userSetting == nil { + return nil, status.Errorf(codes.NotFound, "shortcut not found") + } + + shortcutsUserSetting := userSetting.GetShortcuts() + for _, shortcut := range shortcutsUserSetting.GetShortcuts() { + if shortcut.GetId() == shortcutID { + return &v1pb.Shortcut{ + Name: constructShortcutName(userID, shortcut.GetId()), + Title: shortcut.GetTitle(), + Filter: shortcut.GetFilter(), + }, nil + } + } + + return nil, status.Errorf(codes.NotFound, "shortcut not found") +} + func (s *APIV1Service) CreateShortcut(ctx context.Context, request *v1pb.CreateShortcutRequest) (*v1pb.Shortcut, error) { userID, err := ExtractUserIDFromName(request.Parent) if err != nil { @@ -84,7 +151,7 @@ func (s *APIV1Service) CreateShortcut(ctx context.Context, request *v1pb.CreateS } if request.ValidateOnly { return &v1pb.Shortcut{ - Id: newShortcut.GetId(), + Name: constructShortcutName(userID, newShortcut.GetId()), Title: newShortcut.GetTitle(), Filter: newShortcut.GetFilter(), }, nil @@ -123,16 +190,16 @@ func (s *APIV1Service) CreateShortcut(ctx context.Context, request *v1pb.CreateS } return &v1pb.Shortcut{ - Id: request.Shortcut.GetId(), - Title: request.Shortcut.GetTitle(), - Filter: request.Shortcut.GetFilter(), + Name: constructShortcutName(userID, newShortcut.GetId()), + Title: newShortcut.GetTitle(), + Filter: newShortcut.GetFilter(), }, nil } func (s *APIV1Service) UpdateShortcut(ctx context.Context, request *v1pb.UpdateShortcutRequest) (*v1pb.Shortcut, error) { - userID, err := ExtractUserIDFromName(request.Parent) + userID, shortcutID, err := extractUserAndShortcutIDFromName(request.Shortcut.Name) if err != nil { - return nil, status.Errorf(codes.InvalidArgument, "invalid user name: %v", err) + return nil, status.Errorf(codes.InvalidArgument, "invalid shortcut name: %v", err) } currentUser, err := s.GetCurrentUser(ctx) @@ -159,9 +226,11 @@ func (s *APIV1Service) UpdateShortcut(ctx context.Context, request *v1pb.UpdateS shortcutsUserSetting := userSetting.GetShortcuts() shortcuts := shortcutsUserSetting.GetShortcuts() + var foundShortcut *storepb.ShortcutsUserSetting_Shortcut newShortcuts := make([]*storepb.ShortcutsUserSetting_Shortcut, 0, len(shortcuts)) for _, shortcut := range shortcuts { - if shortcut.GetId() == request.Shortcut.GetId() { + if shortcut.GetId() == shortcutID { + foundShortcut = shortcut for _, field := range request.UpdateMask.Paths { if field == "title" { if request.Shortcut.GetTitle() == "" { @@ -178,6 +247,11 @@ func (s *APIV1Service) UpdateShortcut(ctx context.Context, request *v1pb.UpdateS } newShortcuts = append(newShortcuts, shortcut) } + + if foundShortcut == nil { + return nil, status.Errorf(codes.NotFound, "shortcut not found") + } + shortcutsUserSetting.Shortcuts = newShortcuts userSetting.Value = &storepb.UserSetting_Shortcuts{ Shortcuts: shortcutsUserSetting, @@ -188,16 +262,16 @@ func (s *APIV1Service) UpdateShortcut(ctx context.Context, request *v1pb.UpdateS } return &v1pb.Shortcut{ - Id: request.Shortcut.GetId(), - Title: request.Shortcut.GetTitle(), - Filter: request.Shortcut.GetFilter(), + Name: constructShortcutName(userID, foundShortcut.GetId()), + Title: foundShortcut.GetTitle(), + Filter: foundShortcut.GetFilter(), }, nil } func (s *APIV1Service) DeleteShortcut(ctx context.Context, request *v1pb.DeleteShortcutRequest) (*emptypb.Empty, error) { - userID, err := ExtractUserIDFromName(request.Parent) + userID, shortcutID, err := extractUserAndShortcutIDFromName(request.Name) if err != nil { - return nil, status.Errorf(codes.InvalidArgument, "invalid user name: %v", err) + return nil, status.Errorf(codes.InvalidArgument, "invalid shortcut name: %v", err) } currentUser, err := s.GetCurrentUser(ctx) @@ -223,7 +297,7 @@ func (s *APIV1Service) DeleteShortcut(ctx context.Context, request *v1pb.DeleteS shortcuts := shortcutsUserSetting.GetShortcuts() newShortcuts := make([]*storepb.ShortcutsUserSetting_Shortcut, 0, len(shortcuts)) for _, shortcut := range shortcuts { - if shortcut.GetId() != request.Id { + if shortcut.GetId() != shortcutID { newShortcuts = append(newShortcuts, shortcut) } } diff --git a/web/src/components/CreateShortcutDialog.tsx b/web/src/components/CreateShortcutDialog.tsx index 945695ad6..066338327 100644 --- a/web/src/components/CreateShortcutDialog.tsx +++ b/web/src/components/CreateShortcutDialog.tsx @@ -8,7 +8,6 @@ import useLoading from "@/hooks/useLoading"; import { userStore } from "@/store/v2"; import { Shortcut } from "@/types/proto/api/v1/shortcut_service"; import { useTranslate } from "@/utils/i18n"; -import { generateUUID } from "@/utils/uuid"; import { generateDialog } from "./Dialog"; interface Props extends DialogProps { @@ -20,7 +19,7 @@ const CreateShortcutDialog: React.FC = (props: Props) => { const t = useTranslate(); const user = useCurrentUser(); const [shortcut, setShortcut] = useState({ - id: props.shortcut?.id || "", + name: props.shortcut?.name || "", title: props.shortcut?.title || "", filter: props.shortcut?.filter || "", }); @@ -46,13 +45,20 @@ const CreateShortcutDialog: React.FC = (props: Props) => { await shortcutServiceClient.createShortcut({ parent: user.name, shortcut: { - ...shortcut, - id: generateUUID(), + name: "", // Will be set by server + title: shortcut.title, + filter: shortcut.filter, }, }); toast.success("Create shortcut successfully"); } else { - await shortcutServiceClient.updateShortcut({ parent: user.name, shortcut, updateMask: ["title", "filter"] }); + await shortcutServiceClient.updateShortcut({ + shortcut: { + ...shortcut, + name: props.shortcut!.name, // Keep the original resource name + }, + updateMask: ["title", "filter"], + }); toast.success("Update shortcut successfully"); } // Refresh shortcuts. diff --git a/web/src/components/HomeSidebar/ShortcutsSection.tsx b/web/src/components/HomeSidebar/ShortcutsSection.tsx index 5e64b442c..e4e818010 100644 --- a/web/src/components/HomeSidebar/ShortcutsSection.tsx +++ b/web/src/components/HomeSidebar/ShortcutsSection.tsx @@ -3,7 +3,6 @@ import { Edit3Icon, MoreVerticalIcon, TrashIcon, PlusIcon } from "lucide-react"; import { observer } from "mobx-react-lite"; import { shortcutServiceClient } from "@/grpcweb"; import useAsyncEffect from "@/hooks/useAsyncEffect"; -import useCurrentUser from "@/hooks/useCurrentUser"; import { userStore } from "@/store/v2"; import memoFilterStore from "@/store/v2/memoFilter"; import { Shortcut } from "@/types/proto/api/v1/shortcut_service"; @@ -14,9 +13,15 @@ import { Popover, PopoverContent, PopoverTrigger } from "../ui/Popover"; const emojiRegex = /^(\p{Emoji_Presentation}|\p{Emoji}\uFE0F)$/u; +// Helper function to extract shortcut ID from resource name +// Format: users/{user}/shortcuts/{shortcut} +const getShortcutId = (name: string): string => { + const parts = name.split("/"); + return parts.length === 4 ? parts[3] : ""; +}; + const ShortcutsSection = observer(() => { const t = useTranslate(); - const user = useCurrentUser(); const shortcuts = userStore.state.shortcuts; useAsyncEffect(async () => { @@ -26,7 +31,7 @@ const ShortcutsSection = observer(() => { const handleDeleteShortcut = async (shortcut: Shortcut) => { const confirmed = window.confirm("Are you sure you want to delete this shortcut?"); if (confirmed) { - await shortcutServiceClient.deleteShortcut({ parent: user.name, id: shortcut.id }); + await shortcutServiceClient.deleteShortcut({ name: shortcut.name }); await userStore.fetchShortcuts(); } }; @@ -41,18 +46,19 @@ const ShortcutsSection = observer(() => {
{shortcuts.map((shortcut) => { + const shortcutId = getShortcutId(shortcut.name); const maybeEmoji = shortcut.title.split(" ")[0]; const emoji = emojiRegex.test(maybeEmoji) ? maybeEmoji : undefined; const title = emoji ? shortcut.title.replace(emoji, "") : shortcut.title; - const selected = memoFilterStore.shortcut === shortcut.id; + const selected = memoFilterStore.shortcut === shortcutId; return (
(selected ? memoFilterStore.setShortcut(undefined) : memoFilterStore.setShortcut(shortcut.id))} + onClick={() => (selected ? memoFilterStore.setShortcut(undefined) : memoFilterStore.setShortcut(shortcutId))} > {emoji && {emoji}} {title.trim()} diff --git a/web/src/pages/Home.tsx b/web/src/pages/Home.tsx index c29303e66..dad4ad9bb 100644 --- a/web/src/pages/Home.tsx +++ b/web/src/pages/Home.tsx @@ -9,9 +9,16 @@ import memoFilterStore from "@/store/v2/memoFilter"; import { Direction, State } from "@/types/proto/api/v1/common"; import { Memo } from "@/types/proto/api/v1/memo_service"; +// Helper function to extract shortcut ID from resource name +// Format: users/{user}/shortcuts/{shortcut} +const getShortcutId = (name: string): string => { + const parts = name.split("/"); + return parts.length === 4 ? parts[3] : ""; +}; + const Home = observer(() => { const user = useCurrentUser(); - const selectedShortcut = userStore.state.shortcuts.find((shortcut) => shortcut.id === memoFilterStore.shortcut); + const selectedShortcut = userStore.state.shortcuts.find((shortcut) => getShortcutId(shortcut.name) === memoFilterStore.shortcut); const memoListFilter = useMemo(() => { const conditions = []; diff --git a/web/src/types/proto/api/v1/shortcut_service.ts b/web/src/types/proto/api/v1/shortcut_service.ts index 4859c6097..5d5466af8 100644 --- a/web/src/types/proto/api/v1/shortcut_service.ts +++ b/web/src/types/proto/api/v1/shortcut_service.ts @@ -12,49 +12,85 @@ import { FieldMask } from "../../google/protobuf/field_mask"; export const protobufPackage = "memos.api.v1"; export interface Shortcut { - id: string; + /** + * The resource name of the shortcut. + * Format: users/{user}/shortcuts/{shortcut} + */ + name: string; + /** The title of the shortcut. */ title: string; + /** The filter expression for the shortcut. */ filter: string; } export interface ListShortcutsRequest { - /** The name of the user. */ + /** + * Required. The parent resource where shortcuts are listed. + * Format: users/{user} + */ parent: string; + /** Optional. The maximum number of shortcuts to return. */ + pageSize: number; + /** Optional. A page token for pagination. */ + pageToken: string; } export interface ListShortcutsResponse { + /** The list of shortcuts. */ shortcuts: Shortcut[]; + /** A token for the next page of results. */ + nextPageToken: string; + /** The total count of shortcuts. */ + totalSize: number; +} + +export interface GetShortcutRequest { + /** + * Required. The resource name of the shortcut to retrieve. + * Format: users/{user}/shortcuts/{shortcut} + */ + name: string; } export interface CreateShortcutRequest { - /** The name of the user. */ + /** + * Required. The parent resource where this shortcut will be created. + * Format: users/{user} + */ parent: string; - shortcut?: Shortcut | undefined; + /** Required. The shortcut to create. */ + shortcut?: + | Shortcut + | undefined; + /** Optional. If set, validate the request, but do not actually create the shortcut. */ validateOnly: boolean; } export interface UpdateShortcutRequest { - /** The name of the user. */ - parent: string; - shortcut?: Shortcut | undefined; + /** Required. The shortcut resource which replaces the resource on the server. */ + shortcut?: + | Shortcut + | undefined; + /** Optional. The list of fields to update. */ updateMask?: string[] | undefined; } export interface DeleteShortcutRequest { - /** The name of the user. */ - parent: string; - /** The id of the shortcut. */ - id: string; + /** + * Required. The resource name of the shortcut to delete. + * Format: users/{user}/shortcuts/{shortcut} + */ + name: string; } function createBaseShortcut(): Shortcut { - return { id: "", title: "", filter: "" }; + return { name: "", title: "", filter: "" }; } export const Shortcut: MessageFns = { encode(message: Shortcut, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { - if (message.id !== "") { - writer.uint32(10).string(message.id); + if (message.name !== "") { + writer.uint32(10).string(message.name); } if (message.title !== "") { writer.uint32(18).string(message.title); @@ -77,7 +113,7 @@ export const Shortcut: MessageFns = { break; } - message.id = reader.string(); + message.name = reader.string(); continue; } case 2: { @@ -110,7 +146,7 @@ export const Shortcut: MessageFns = { }, fromPartial(object: DeepPartial): Shortcut { const message = createBaseShortcut(); - message.id = object.id ?? ""; + message.name = object.name ?? ""; message.title = object.title ?? ""; message.filter = object.filter ?? ""; return message; @@ -118,7 +154,7 @@ export const Shortcut: MessageFns = { }; function createBaseListShortcutsRequest(): ListShortcutsRequest { - return { parent: "" }; + return { parent: "", pageSize: 0, pageToken: "" }; } export const ListShortcutsRequest: MessageFns = { @@ -126,6 +162,12 @@ export const ListShortcutsRequest: MessageFns = { if (message.parent !== "") { writer.uint32(10).string(message.parent); } + if (message.pageSize !== 0) { + writer.uint32(16).int32(message.pageSize); + } + if (message.pageToken !== "") { + writer.uint32(26).string(message.pageToken); + } return writer; }, @@ -144,6 +186,22 @@ export const ListShortcutsRequest: MessageFns = { message.parent = reader.string(); continue; } + case 2: { + if (tag !== 16) { + break; + } + + message.pageSize = reader.int32(); + continue; + } + case 3: { + if (tag !== 26) { + break; + } + + message.pageToken = reader.string(); + continue; + } } if ((tag & 7) === 4 || tag === 0) { break; @@ -159,12 +217,14 @@ export const ListShortcutsRequest: MessageFns = { fromPartial(object: DeepPartial): ListShortcutsRequest { const message = createBaseListShortcutsRequest(); message.parent = object.parent ?? ""; + message.pageSize = object.pageSize ?? 0; + message.pageToken = object.pageToken ?? ""; return message; }, }; function createBaseListShortcutsResponse(): ListShortcutsResponse { - return { shortcuts: [] }; + return { shortcuts: [], nextPageToken: "", totalSize: 0 }; } export const ListShortcutsResponse: MessageFns = { @@ -172,6 +232,12 @@ export const ListShortcutsResponse: MessageFns = { for (const v of message.shortcuts) { Shortcut.encode(v!, writer.uint32(10).fork()).join(); } + if (message.nextPageToken !== "") { + writer.uint32(18).string(message.nextPageToken); + } + if (message.totalSize !== 0) { + writer.uint32(24).int32(message.totalSize); + } return writer; }, @@ -190,6 +256,22 @@ export const ListShortcutsResponse: MessageFns = { message.shortcuts.push(Shortcut.decode(reader, reader.uint32())); continue; } + case 2: { + if (tag !== 18) { + break; + } + + message.nextPageToken = reader.string(); + continue; + } + case 3: { + if (tag !== 24) { + break; + } + + message.totalSize = reader.int32(); + continue; + } } if ((tag & 7) === 4 || tag === 0) { break; @@ -205,6 +287,54 @@ export const ListShortcutsResponse: MessageFns = { fromPartial(object: DeepPartial): ListShortcutsResponse { const message = createBaseListShortcutsResponse(); message.shortcuts = object.shortcuts?.map((e) => Shortcut.fromPartial(e)) || []; + message.nextPageToken = object.nextPageToken ?? ""; + message.totalSize = object.totalSize ?? 0; + return message; + }, +}; + +function createBaseGetShortcutRequest(): GetShortcutRequest { + return { name: "" }; +} + +export const GetShortcutRequest: MessageFns = { + encode(message: GetShortcutRequest, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.name !== "") { + writer.uint32(10).string(message.name); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): GetShortcutRequest { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseGetShortcutRequest(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 10) { + break; + } + + message.name = reader.string(); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + create(base?: DeepPartial): GetShortcutRequest { + return GetShortcutRequest.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): GetShortcutRequest { + const message = createBaseGetShortcutRequest(); + message.name = object.name ?? ""; return message; }, }; @@ -282,19 +412,16 @@ export const CreateShortcutRequest: MessageFns = { }; function createBaseUpdateShortcutRequest(): UpdateShortcutRequest { - return { parent: "", shortcut: undefined, updateMask: undefined }; + return { shortcut: undefined, updateMask: undefined }; } export const UpdateShortcutRequest: MessageFns = { encode(message: UpdateShortcutRequest, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { - if (message.parent !== "") { - writer.uint32(10).string(message.parent); - } if (message.shortcut !== undefined) { - Shortcut.encode(message.shortcut, writer.uint32(18).fork()).join(); + Shortcut.encode(message.shortcut, writer.uint32(10).fork()).join(); } if (message.updateMask !== undefined) { - FieldMask.encode(FieldMask.wrap(message.updateMask), writer.uint32(26).fork()).join(); + FieldMask.encode(FieldMask.wrap(message.updateMask), writer.uint32(18).fork()).join(); } return writer; }, @@ -311,7 +438,7 @@ export const UpdateShortcutRequest: MessageFns = { break; } - message.parent = reader.string(); + message.shortcut = Shortcut.decode(reader, reader.uint32()); continue; } case 2: { @@ -319,14 +446,6 @@ export const UpdateShortcutRequest: MessageFns = { break; } - message.shortcut = Shortcut.decode(reader, reader.uint32()); - continue; - } - case 3: { - if (tag !== 26) { - break; - } - message.updateMask = FieldMask.unwrap(FieldMask.decode(reader, reader.uint32())); continue; } @@ -344,7 +463,6 @@ export const UpdateShortcutRequest: MessageFns = { }, fromPartial(object: DeepPartial): UpdateShortcutRequest { const message = createBaseUpdateShortcutRequest(); - message.parent = object.parent ?? ""; message.shortcut = (object.shortcut !== undefined && object.shortcut !== null) ? Shortcut.fromPartial(object.shortcut) : undefined; @@ -354,16 +472,13 @@ export const UpdateShortcutRequest: MessageFns = { }; function createBaseDeleteShortcutRequest(): DeleteShortcutRequest { - return { parent: "", id: "" }; + return { name: "" }; } export const DeleteShortcutRequest: MessageFns = { encode(message: DeleteShortcutRequest, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { - if (message.parent !== "") { - writer.uint32(10).string(message.parent); - } - if (message.id !== "") { - writer.uint32(18).string(message.id); + if (message.name !== "") { + writer.uint32(10).string(message.name); } return writer; }, @@ -380,15 +495,7 @@ export const DeleteShortcutRequest: MessageFns = { break; } - message.parent = reader.string(); - continue; - } - case 2: { - if (tag !== 18) { - break; - } - - message.id = reader.string(); + message.name = reader.string(); continue; } } @@ -405,8 +512,7 @@ export const DeleteShortcutRequest: MessageFns = { }, fromPartial(object: DeepPartial): DeleteShortcutRequest { const message = createBaseDeleteShortcutRequest(); - message.parent = object.parent ?? ""; - message.id = object.id ?? ""; + message.name = object.name ?? ""; return message; }, }; @@ -470,6 +576,60 @@ export const ShortcutServiceDefinition = { }, }, }, + /** GetShortcut gets a shortcut by name. */ + getShortcut: { + name: "GetShortcut", + requestType: GetShortcutRequest, + requestStream: false, + responseType: Shortcut, + responseStream: false, + options: { + _unknownFields: { + 8410: [new Uint8Array([4, 110, 97, 109, 101])], + 578365826: [ + new Uint8Array([ + 36, + 18, + 34, + 47, + 97, + 112, + 105, + 47, + 118, + 49, + 47, + 123, + 110, + 97, + 109, + 101, + 61, + 117, + 115, + 101, + 114, + 115, + 47, + 42, + 47, + 115, + 104, + 111, + 114, + 116, + 99, + 117, + 116, + 115, + 47, + 42, + 125, + ]), + ], + }, + }, + }, /** CreateShortcut creates a new shortcut for a user. */ createShortcut: { name: "CreateShortcut", @@ -545,14 +705,7 @@ export const ShortcutServiceDefinition = { _unknownFields: { 8410: [ new Uint8Array([ - 27, - 112, - 97, - 114, - 101, - 110, - 116, - 44, + 20, 115, 104, 111, @@ -577,7 +730,7 @@ export const ShortcutServiceDefinition = { ], 578365826: [ new Uint8Array([ - 60, + 55, 58, 8, 115, @@ -589,7 +742,7 @@ export const ShortcutServiceDefinition = { 117, 116, 50, - 48, + 43, 47, 97, 112, @@ -599,12 +752,19 @@ export const ShortcutServiceDefinition = { 49, 47, 123, - 112, - 97, + 115, + 104, + 111, 114, - 101, - 110, 116, + 99, + 117, + 116, + 46, + 110, + 97, + 109, + 101, 61, 117, 115, @@ -613,7 +773,6 @@ export const ShortcutServiceDefinition = { 115, 47, 42, - 125, 47, 115, 104, @@ -625,18 +784,7 @@ export const ShortcutServiceDefinition = { 116, 115, 47, - 123, - 115, - 104, - 111, - 114, - 116, - 99, - 117, - 116, - 46, - 105, - 100, + 42, 125, ]), ], @@ -652,12 +800,12 @@ export const ShortcutServiceDefinition = { responseStream: false, options: { _unknownFields: { - 8410: [new Uint8Array([9, 112, 97, 114, 101, 110, 116, 44, 105, 100])], + 8410: [new Uint8Array([4, 110, 97, 109, 101])], 578365826: [ new Uint8Array([ - 41, + 36, 42, - 39, + 34, 47, 97, 112, @@ -667,12 +815,10 @@ export const ShortcutServiceDefinition = { 49, 47, 123, - 112, + 110, 97, - 114, + 109, 101, - 110, - 116, 61, 117, 115, @@ -681,7 +827,6 @@ export const ShortcutServiceDefinition = { 115, 47, 42, - 125, 47, 115, 104, @@ -693,9 +838,7 @@ export const ShortcutServiceDefinition = { 116, 115, 47, - 123, - 105, - 100, + 42, 125, ]), ],