Commit 14c72fa7 authored by johnnyjoy's avatar johnnyjoy

feat: implement shortcuts

parent b9a0c561
...@@ -94,6 +94,32 @@ service UserService { ...@@ -94,6 +94,32 @@ service UserService {
option (google.api.http) = {delete: "/api/v1/{name=users/*}/access_tokens/{access_token}"}; option (google.api.http) = {delete: "/api/v1/{name=users/*}/access_tokens/{access_token}"};
option (google.api.method_signature) = "name,access_token"; option (google.api.method_signature) = "name,access_token";
} }
// ListShortcuts returns a list of shortcuts for a user.
rpc ListShortcuts(ListShortcutsRequest) returns (ListShortcutsResponse) {
option (google.api.http) = {get: "/api/v1/{parent=users/*}/shortcuts"};
option (google.api.method_signature) = "parent";
}
// CreateShortcut creates a new shortcut for a user.
rpc CreateShortcut(CreateShortcutRequest) returns (Shortcut) {
option (google.api.http) = {
post: "/api/v1/{parent=users/*}/shortcuts"
body: "shortcut"
};
option (google.api.method_signature) = "parent,shortcut";
}
// UpdateShortcut updates a shortcut for a user.
rpc UpdateShortcut(UpdateShortcutRequest) returns (Shortcut) {
option (google.api.http) = {
patch: "/api/v1/{parent=users/*}/shortcuts/{shortcut.id}"
body: "shortcut"
};
option (google.api.method_signature) = "parent,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";
}
} }
message User { message User {
...@@ -254,3 +280,42 @@ message DeleteUserAccessTokenRequest { ...@@ -254,3 +280,42 @@ message DeleteUserAccessTokenRequest {
// access_token is the access token to delete. // access_token is the access token to delete.
string access_token = 2; string access_token = 2;
} }
message Shortcut {
string id = 1;
string title = 2;
string filter = 3;
}
message ListShortcutsRequest {
// The name of the user.
string parent = 1;
}
message ListShortcutsResponse {
repeated Shortcut shortcuts = 1;
}
message CreateShortcutRequest {
// The name of the user.
string parent = 1;
Shortcut shortcut = 2;
}
message UpdateShortcutRequest {
// The name of the user.
string parent = 1;
Shortcut shortcut = 2;
google.protobuf.FieldMask update_mask = 3;
}
message DeleteShortcutRequest {
// The name of the user.
string parent = 1;
// The id of the shortcut.
string id = 2;
}
This diff is collapsed.
This diff is collapsed.
...@@ -35,6 +35,10 @@ const ( ...@@ -35,6 +35,10 @@ const (
UserService_ListUserAccessTokens_FullMethodName = "/memos.api.v1.UserService/ListUserAccessTokens" UserService_ListUserAccessTokens_FullMethodName = "/memos.api.v1.UserService/ListUserAccessTokens"
UserService_CreateUserAccessToken_FullMethodName = "/memos.api.v1.UserService/CreateUserAccessToken" UserService_CreateUserAccessToken_FullMethodName = "/memos.api.v1.UserService/CreateUserAccessToken"
UserService_DeleteUserAccessToken_FullMethodName = "/memos.api.v1.UserService/DeleteUserAccessToken" UserService_DeleteUserAccessToken_FullMethodName = "/memos.api.v1.UserService/DeleteUserAccessToken"
UserService_ListShortcuts_FullMethodName = "/memos.api.v1.UserService/ListShortcuts"
UserService_CreateShortcut_FullMethodName = "/memos.api.v1.UserService/CreateShortcut"
UserService_UpdateShortcut_FullMethodName = "/memos.api.v1.UserService/UpdateShortcut"
UserService_DeleteShortcut_FullMethodName = "/memos.api.v1.UserService/DeleteShortcut"
) )
// UserServiceClient is the client API for UserService service. // UserServiceClient is the client API for UserService service.
...@@ -69,6 +73,14 @@ type UserServiceClient interface { ...@@ -69,6 +73,14 @@ type UserServiceClient interface {
CreateUserAccessToken(ctx context.Context, in *CreateUserAccessTokenRequest, opts ...grpc.CallOption) (*UserAccessToken, error) CreateUserAccessToken(ctx context.Context, in *CreateUserAccessTokenRequest, opts ...grpc.CallOption) (*UserAccessToken, error)
// DeleteUserAccessToken deletes an access token for a user. // DeleteUserAccessToken deletes an access token for a user.
DeleteUserAccessToken(ctx context.Context, in *DeleteUserAccessTokenRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) DeleteUserAccessToken(ctx context.Context, in *DeleteUserAccessTokenRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
// ListShortcuts returns a list of shortcuts for a user.
ListShortcuts(ctx context.Context, in *ListShortcutsRequest, opts ...grpc.CallOption) (*ListShortcutsResponse, 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.
UpdateShortcut(ctx context.Context, in *UpdateShortcutRequest, opts ...grpc.CallOption) (*Shortcut, error)
// DeleteShortcut deletes a shortcut for a user.
DeleteShortcut(ctx context.Context, in *DeleteShortcutRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
} }
type userServiceClient struct { type userServiceClient struct {
...@@ -219,6 +231,46 @@ func (c *userServiceClient) DeleteUserAccessToken(ctx context.Context, in *Delet ...@@ -219,6 +231,46 @@ func (c *userServiceClient) DeleteUserAccessToken(ctx context.Context, in *Delet
return out, nil return out, nil
} }
func (c *userServiceClient) ListShortcuts(ctx context.Context, in *ListShortcutsRequest, opts ...grpc.CallOption) (*ListShortcutsResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(ListShortcutsResponse)
err := c.cc.Invoke(ctx, UserService_ListShortcuts_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *userServiceClient) CreateShortcut(ctx context.Context, in *CreateShortcutRequest, opts ...grpc.CallOption) (*Shortcut, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(Shortcut)
err := c.cc.Invoke(ctx, UserService_CreateShortcut_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *userServiceClient) UpdateShortcut(ctx context.Context, in *UpdateShortcutRequest, opts ...grpc.CallOption) (*Shortcut, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(Shortcut)
err := c.cc.Invoke(ctx, UserService_UpdateShortcut_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *userServiceClient) DeleteShortcut(ctx context.Context, in *DeleteShortcutRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(emptypb.Empty)
err := c.cc.Invoke(ctx, UserService_DeleteShortcut_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
// UserServiceServer is the server API for UserService service. // UserServiceServer is the server API for UserService service.
// All implementations must embed UnimplementedUserServiceServer // All implementations must embed UnimplementedUserServiceServer
// for forward compatibility. // for forward compatibility.
...@@ -251,6 +303,14 @@ type UserServiceServer interface { ...@@ -251,6 +303,14 @@ type UserServiceServer interface {
CreateUserAccessToken(context.Context, *CreateUserAccessTokenRequest) (*UserAccessToken, error) CreateUserAccessToken(context.Context, *CreateUserAccessTokenRequest) (*UserAccessToken, error)
// DeleteUserAccessToken deletes an access token for a user. // DeleteUserAccessToken deletes an access token for a user.
DeleteUserAccessToken(context.Context, *DeleteUserAccessTokenRequest) (*emptypb.Empty, error) DeleteUserAccessToken(context.Context, *DeleteUserAccessTokenRequest) (*emptypb.Empty, error)
// ListShortcuts returns a list of shortcuts for a user.
ListShortcuts(context.Context, *ListShortcutsRequest) (*ListShortcutsResponse, error)
// CreateShortcut creates a new shortcut for a user.
CreateShortcut(context.Context, *CreateShortcutRequest) (*Shortcut, error)
// UpdateShortcut updates a shortcut for a user.
UpdateShortcut(context.Context, *UpdateShortcutRequest) (*Shortcut, error)
// DeleteShortcut deletes a shortcut for a user.
DeleteShortcut(context.Context, *DeleteShortcutRequest) (*emptypb.Empty, error)
mustEmbedUnimplementedUserServiceServer() mustEmbedUnimplementedUserServiceServer()
} }
...@@ -303,6 +363,18 @@ func (UnimplementedUserServiceServer) CreateUserAccessToken(context.Context, *Cr ...@@ -303,6 +363,18 @@ func (UnimplementedUserServiceServer) CreateUserAccessToken(context.Context, *Cr
func (UnimplementedUserServiceServer) DeleteUserAccessToken(context.Context, *DeleteUserAccessTokenRequest) (*emptypb.Empty, error) { func (UnimplementedUserServiceServer) DeleteUserAccessToken(context.Context, *DeleteUserAccessTokenRequest) (*emptypb.Empty, error) {
return nil, status.Errorf(codes.Unimplemented, "method DeleteUserAccessToken not implemented") return nil, status.Errorf(codes.Unimplemented, "method DeleteUserAccessToken not implemented")
} }
func (UnimplementedUserServiceServer) ListShortcuts(context.Context, *ListShortcutsRequest) (*ListShortcutsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListShortcuts not implemented")
}
func (UnimplementedUserServiceServer) CreateShortcut(context.Context, *CreateShortcutRequest) (*Shortcut, error) {
return nil, status.Errorf(codes.Unimplemented, "method CreateShortcut not implemented")
}
func (UnimplementedUserServiceServer) UpdateShortcut(context.Context, *UpdateShortcutRequest) (*Shortcut, error) {
return nil, status.Errorf(codes.Unimplemented, "method UpdateShortcut not implemented")
}
func (UnimplementedUserServiceServer) DeleteShortcut(context.Context, *DeleteShortcutRequest) (*emptypb.Empty, error) {
return nil, status.Errorf(codes.Unimplemented, "method DeleteShortcut not implemented")
}
func (UnimplementedUserServiceServer) mustEmbedUnimplementedUserServiceServer() {} func (UnimplementedUserServiceServer) mustEmbedUnimplementedUserServiceServer() {}
func (UnimplementedUserServiceServer) testEmbeddedByValue() {} func (UnimplementedUserServiceServer) testEmbeddedByValue() {}
...@@ -576,6 +648,78 @@ func _UserService_DeleteUserAccessToken_Handler(srv interface{}, ctx context.Con ...@@ -576,6 +648,78 @@ func _UserService_DeleteUserAccessToken_Handler(srv interface{}, ctx context.Con
return interceptor(ctx, in, info, handler) return interceptor(ctx, in, info, handler)
} }
func _UserService_ListShortcuts_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ListShortcutsRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(UserServiceServer).ListShortcuts(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: UserService_ListShortcuts_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(UserServiceServer).ListShortcuts(ctx, req.(*ListShortcutsRequest))
}
return interceptor(ctx, in, info, handler)
}
func _UserService_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 {
return nil, err
}
if interceptor == nil {
return srv.(UserServiceServer).CreateShortcut(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: UserService_CreateShortcut_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(UserServiceServer).CreateShortcut(ctx, req.(*CreateShortcutRequest))
}
return interceptor(ctx, in, info, handler)
}
func _UserService_UpdateShortcut_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(UpdateShortcutRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(UserServiceServer).UpdateShortcut(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: UserService_UpdateShortcut_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(UserServiceServer).UpdateShortcut(ctx, req.(*UpdateShortcutRequest))
}
return interceptor(ctx, in, info, handler)
}
func _UserService_DeleteShortcut_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(DeleteShortcutRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(UserServiceServer).DeleteShortcut(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: UserService_DeleteShortcut_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(UserServiceServer).DeleteShortcut(ctx, req.(*DeleteShortcutRequest))
}
return interceptor(ctx, in, info, handler)
}
// UserService_ServiceDesc is the grpc.ServiceDesc for UserService service. // UserService_ServiceDesc is the grpc.ServiceDesc for UserService service.
// It's only intended for direct use with grpc.RegisterService, // It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy) // and not to be introspected or modified (even as a copy)
...@@ -639,6 +783,22 @@ var UserService_ServiceDesc = grpc.ServiceDesc{ ...@@ -639,6 +783,22 @@ var UserService_ServiceDesc = grpc.ServiceDesc{
MethodName: "DeleteUserAccessToken", MethodName: "DeleteUserAccessToken",
Handler: _UserService_DeleteUserAccessToken_Handler, Handler: _UserService_DeleteUserAccessToken_Handler,
}, },
{
MethodName: "ListShortcuts",
Handler: _UserService_ListShortcuts_Handler,
},
{
MethodName: "CreateShortcut",
Handler: _UserService_CreateShortcut_Handler,
},
{
MethodName: "UpdateShortcut",
Handler: _UserService_UpdateShortcut_Handler,
},
{
MethodName: "DeleteShortcut",
Handler: _UserService_DeleteShortcut_Handler,
},
}, },
Streams: []grpc.StreamDesc{}, Streams: []grpc.StreamDesc{},
Metadata: "api/v1/user_service.proto", Metadata: "api/v1/user_service.proto",
......
...@@ -1406,6 +1406,118 @@ paths: ...@@ -1406,6 +1406,118 @@ paths:
pattern: users/[^/]+ pattern: users/[^/]+
tags: tags:
- UserService - UserService
/api/v1/{parent}/shortcuts:
get:
summary: ListShortcuts returns a list of shortcuts for a user.
operationId: UserService_ListShortcuts
responses:
"200":
description: A successful response.
schema:
$ref: '#/definitions/v1ListShortcutsResponse'
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/[^/]+
tags:
- UserService
post:
summary: CreateShortcut creates a new shortcut for a user.
operationId: UserService_CreateShortcut
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
in: body
required: true
schema:
$ref: '#/definitions/apiv1Shortcut'
tags:
- UserService
/api/v1/{parent}/shortcuts/{id}:
delete:
summary: DeleteShortcut deletes a shortcut for a user.
operationId: UserService_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:
- UserService
/api/v1/{parent}/shortcuts/{shortcut.id}:
patch:
summary: UpdateShortcut updates a shortcut for a user.
operationId: UserService_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:
- UserService
/api/v1/{parent}/tags/{tag}: /api/v1/{parent}/tags/{tag}:
delete: delete:
summary: DeleteMemoTag deletes a tag for a memo. summary: DeleteMemoTag deletes a tag for a memo.
...@@ -1997,6 +2109,15 @@ definitions: ...@@ -1997,6 +2109,15 @@ definitions:
type: string type: string
fieldMapping: fieldMapping:
$ref: '#/definitions/apiv1FieldMapping' $ref: '#/definitions/apiv1FieldMapping'
apiv1Shortcut:
type: object
properties:
id:
type: string
title:
type: string
filter:
type: string
apiv1UserSetting: apiv1UserSetting:
type: object type: object
properties: properties:
...@@ -2558,6 +2679,14 @@ definitions: ...@@ -2558,6 +2679,14 @@ definitions:
items: items:
type: object type: object
$ref: '#/definitions/v1Resource' $ref: '#/definitions/v1Resource'
v1ListShortcutsResponse:
type: object
properties:
shortcuts:
type: array
items:
type: object
$ref: '#/definitions/apiv1Shortcut'
v1ListUserAccessTokensResponse: v1ListUserAccessTokensResponse:
type: object type: object
properties: properties:
......
This diff is collapsed.
...@@ -14,6 +14,8 @@ enum UserSettingKey { ...@@ -14,6 +14,8 @@ enum UserSettingKey {
APPEARANCE = 3; APPEARANCE = 3;
// The visibility of the memo. // The visibility of the memo.
MEMO_VISIBILITY = 4; MEMO_VISIBILITY = 4;
// The shortcuts of the user.
SHORTCUTS = 5;
} }
message UserSetting { message UserSetting {
...@@ -24,6 +26,7 @@ message UserSetting { ...@@ -24,6 +26,7 @@ message UserSetting {
string locale = 4; string locale = 4;
string appearance = 5; string appearance = 5;
string memo_visibility = 6; string memo_visibility = 6;
ShortcutsUserSetting shortcuts = 7;
} }
} }
...@@ -37,3 +40,12 @@ message AccessTokensUserSetting { ...@@ -37,3 +40,12 @@ message AccessTokensUserSetting {
} }
repeated AccessToken access_tokens = 1; repeated AccessToken access_tokens = 1;
} }
message ShortcutsUserSetting {
message Shortcut {
string id = 1;
string title = 2;
string filter = 3;
}
repeated Shortcut shortcuts = 1;
}
package v1
import (
"context"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/types/known/emptypb"
v1pb "github.com/usememos/memos/proto/gen/api/v1"
storepb "github.com/usememos/memos/proto/gen/store"
"github.com/usememos/memos/store"
)
func (s *APIV1Service) ListShortcuts(ctx context.Context, request *v1pb.ListShortcutsRequest) (*v1pb.ListShortcutsResponse, error) {
userID, err := ExtractUserIDFromName(request.Parent)
if err != nil {
return nil, status.Errorf(codes.InvalidArgument, "invalid user 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 &v1pb.ListShortcutsResponse{
Shortcuts: []*v1pb.Shortcut{},
}, nil
}
shortcutsUserSetting := userSetting.GetShortcuts()
shortcuts := []*v1pb.Shortcut{}
for _, shortcut := range shortcutsUserSetting.GetShortcuts() {
shortcuts = append(shortcuts, &v1pb.Shortcut{
Id: shortcut.GetId(),
Title: shortcut.GetTitle(),
Filter: shortcut.GetFilter(),
})
}
return &v1pb.ListShortcutsResponse{
Shortcuts: shortcuts,
}, nil
}
func (s *APIV1Service) CreateShortcut(ctx context.Context, request *v1pb.CreateShortcutRequest) (*v1pb.Shortcut, error) {
userID, err := ExtractUserIDFromName(request.Parent)
if err != nil {
return nil, status.Errorf(codes.InvalidArgument, "invalid user 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 {
userSetting = &storepb.UserSetting{
UserId: userID,
Key: storepb.UserSettingKey_SHORTCUTS,
Value: &storepb.UserSetting_Shortcuts{},
}
}
shortcutsUserSetting := userSetting.GetShortcuts()
shortcuts := shortcutsUserSetting.GetShortcuts()
shortcuts = append(shortcuts, &storepb.ShortcutsUserSetting_Shortcut{
Id: request.Shortcut.GetId(),
Title: request.Shortcut.GetTitle(),
Filter: request.Shortcut.GetFilter(),
})
shortcutsUserSetting.Shortcuts = shortcuts
userSetting.Value = &storepb.UserSetting_Shortcuts{
Shortcuts: shortcutsUserSetting,
}
_, err = s.Store.UpsertUserSetting(ctx, userSetting)
if err != nil {
return nil, err
}
return &v1pb.Shortcut{
Id: request.Shortcut.GetId(),
Title: request.Shortcut.GetTitle(),
Filter: request.Shortcut.GetFilter(),
}, nil
}
func (s *APIV1Service) UpdateShortcut(ctx context.Context, request *v1pb.UpdateShortcutRequest) (*v1pb.Shortcut, error) {
userID, err := ExtractUserIDFromName(request.Parent)
if err != nil {
return nil, status.Errorf(codes.InvalidArgument, "invalid user 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")
}
if request.UpdateMask == nil || len(request.UpdateMask.Paths) == 0 {
return nil, status.Errorf(codes.InvalidArgument, "update mask is required")
}
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()
shortcuts := shortcutsUserSetting.GetShortcuts()
newShortcuts := make([]*storepb.ShortcutsUserSetting_Shortcut, 0, len(shortcuts))
for _, shortcut := range shortcuts {
if shortcut.GetId() == request.Shortcut.GetId() {
for _, field := range request.UpdateMask.Paths {
if field == "title" {
shortcut.Title = request.Shortcut.GetTitle()
} else if field == "filter" {
shortcut.Filter = request.Shortcut.GetFilter()
}
}
}
newShortcuts = append(newShortcuts, shortcut)
}
shortcutsUserSetting.Shortcuts = newShortcuts
userSetting.Value = &storepb.UserSetting_Shortcuts{
Shortcuts: shortcutsUserSetting,
}
_, err = s.Store.UpsertUserSetting(ctx, userSetting)
if err != nil {
return nil, err
}
return &v1pb.Shortcut{
Id: request.Shortcut.GetId(),
Title: request.Shortcut.GetTitle(),
Filter: request.Shortcut.GetFilter(),
}, nil
}
func (s *APIV1Service) DeleteShortcut(ctx context.Context, request *v1pb.DeleteShortcutRequest) (*emptypb.Empty, error) {
userID, err := ExtractUserIDFromName(request.Parent)
if err != nil {
return nil, status.Errorf(codes.InvalidArgument, "invalid user 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 &emptypb.Empty{}, nil
}
shortcutsUserSetting := userSetting.GetShortcuts()
shortcuts := shortcutsUserSetting.GetShortcuts()
newShortcuts := make([]*storepb.ShortcutsUserSetting_Shortcut, 0, len(shortcuts))
for _, shortcut := range shortcuts {
if shortcut.GetId() != request.Id {
newShortcuts = append(newShortcuts, shortcut)
}
}
shortcutsUserSetting.Shortcuts = newShortcuts
userSetting.Value = &storepb.UserSetting_Shortcuts{
Shortcuts: shortcutsUserSetting,
}
_, err = s.Store.UpsertUserSetting(ctx, userSetting)
if err != nil {
return nil, err
}
return &emptypb.Empty{}, nil
}
...@@ -145,6 +145,12 @@ func convertUserSettingFromRaw(raw *UserSetting) (*storepb.UserSetting, error) { ...@@ -145,6 +145,12 @@ func convertUserSettingFromRaw(raw *UserSetting) (*storepb.UserSetting, error) {
return nil, err return nil, err
} }
userSetting.Value = &storepb.UserSetting_AccessTokens{AccessTokens: accessTokensUserSetting} userSetting.Value = &storepb.UserSetting_AccessTokens{AccessTokens: accessTokensUserSetting}
case storepb.UserSettingKey_SHORTCUTS:
shortcutsUserSetting := &storepb.ShortcutsUserSetting{}
if err := protojsonUnmarshaler.Unmarshal([]byte(raw.Value), shortcutsUserSetting); err != nil {
return nil, err
}
userSetting.Value = &storepb.UserSetting_Shortcuts{Shortcuts: shortcutsUserSetting}
case storepb.UserSettingKey_LOCALE: case storepb.UserSettingKey_LOCALE:
userSetting.Value = &storepb.UserSetting_Locale{Locale: raw.Value} userSetting.Value = &storepb.UserSetting_Locale{Locale: raw.Value}
case storepb.UserSettingKey_APPEARANCE: case storepb.UserSettingKey_APPEARANCE:
...@@ -171,6 +177,13 @@ func convertUserSettingToRaw(userSetting *storepb.UserSetting) (*UserSetting, er ...@@ -171,6 +177,13 @@ func convertUserSettingToRaw(userSetting *storepb.UserSetting) (*UserSetting, er
return nil, err return nil, err
} }
raw.Value = string(value) raw.Value = string(value)
case storepb.UserSettingKey_SHORTCUTS:
shortcutsUserSetting := userSetting.GetShortcuts()
value, err := protojson.Marshal(shortcutsUserSetting)
if err != nil {
return nil, err
}
raw.Value = string(value)
case storepb.UserSettingKey_LOCALE: case storepb.UserSettingKey_LOCALE:
raw.Value = userSetting.GetLocale() raw.Value = userSetting.GetLocale()
case storepb.UserSettingKey_APPEARANCE: case storepb.UserSettingKey_APPEARANCE:
......
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