refactor: update visibilities

This commit is contained in:
Steven
2024-08-31 22:04:24 +08:00
parent 8511c09c63
commit b74e4f90c1
43 changed files with 693 additions and 775 deletions

View File

@ -116,7 +116,7 @@ func (in *GRPCAuthInterceptor) authenticate(ctx context.Context, accessToken str
if user == nil {
return 0, status.Errorf(codes.Unauthenticated, "user ID %q not exists in the access token", userID)
}
if user.RowStatus == store.Archived {
if user.RowStatus == storepb.RowStatus_ARCHIVED {
return 0, status.Errorf(codes.Unauthenticated, "user ID %q has been deactivated by administrators", userID)
}

View File

@ -28,7 +28,7 @@ const (
func (s *APIV1Service) GetAuthStatus(ctx context.Context, _ *v1pb.GetAuthStatusRequest) (*v1pb.User, error) {
user, err := getCurrentUser(ctx, s.Store)
if err != nil {
return nil, status.Errorf(codes.Unauthenticated, "failed to get current user: %v", err)
return nil, status.Errorf(codes.Internal, "failed to get current user: %v", err)
}
if user == nil {
return nil, status.Errorf(codes.Unauthenticated, "user not found")
@ -58,7 +58,7 @@ func (s *APIV1Service) SignIn(ctx context.Context, request *v1pb.SignInRequest)
if workspaceSecuritySetting.DisallowPasswordAuth && user.Role == store.RoleUser {
return nil, status.Errorf(codes.PermissionDenied, "password authentication is not allowed")
}
if user.RowStatus == store.Archived {
if user.RowStatus == storepb.RowStatus_ARCHIVED {
return nil, status.Errorf(codes.PermissionDenied, fmt.Sprintf("user has been archived with email %s", request.Email))
}
@ -143,7 +143,7 @@ func (s *APIV1Service) SignInWithSSO(ctx context.Context, request *v1pb.SignInWi
return nil, status.Errorf(codes.Internal, fmt.Sprintf("failed to create user, err: %s", err))
}
}
if user.RowStatus == store.Archived {
if user.RowStatus == storepb.RowStatus_ARCHIVED {
return nil, status.Errorf(codes.PermissionDenied, fmt.Sprintf("user has been archived with email %s", email))
}

View File

@ -16,31 +16,11 @@ import (
)
func (s *APIV1Service) ListCollections(ctx context.Context, _ *v1pb.ListCollectionsRequest) (*v1pb.ListCollectionsResponse, error) {
user, err := getCurrentUser(ctx, s.Store)
if err != nil {
return nil, status.Errorf(codes.Unauthenticated, "failed to get current user: %v", err)
}
collections, err := s.Store.ListCollections(ctx, &store.FindCollection{
CreatorID: &user.ID,
VisibilityList: []store.Visibility{
store.VisibilityPrivate,
},
})
collections, err := s.Store.ListCollections(ctx, &store.FindCollection{})
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to get collection list, err: %v", err)
}
sharedCollections, err := s.Store.ListCollections(ctx, &store.FindCollection{
VisibilityList: []store.Visibility{
store.VisibilityWorkspace,
store.VisibilityPublic,
},
})
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to get collection list, err: %v", err)
}
collections = append(collections, sharedCollections...)
convertedCollections := []*v1pb.Collection{}
for _, collection := range collections {
convertedCollections = append(convertedCollections, convertCollectionFromStore(collection))
@ -65,9 +45,9 @@ func (s *APIV1Service) GetCollection(ctx context.Context, request *v1pb.GetColle
user, err := getCurrentUser(ctx, s.Store)
if err != nil {
return nil, status.Errorf(codes.Unauthenticated, "failed to get current user: %v", err)
return nil, status.Errorf(codes.Internal, "failed to get current user: %v", err)
}
if collection.Visibility == storepb.Visibility_PRIVATE && collection.CreatorId != user.ID {
if user == nil && collection.Visibility != storepb.Visibility_PUBLIC {
return nil, status.Errorf(codes.PermissionDenied, "Permission denied")
}
return convertCollectionFromStore(collection), nil
@ -84,15 +64,12 @@ func (s *APIV1Service) GetCollectionByName(ctx context.Context, request *v1pb.Ge
return nil, status.Errorf(codes.NotFound, "collection not found")
}
userID, ok := ctx.Value(userIDContextKey).(int32)
if ok {
if collection.Visibility == storepb.Visibility_PRIVATE && collection.CreatorId != userID {
return nil, status.Errorf(codes.PermissionDenied, "Permission denied")
}
} else {
if collection.Visibility != storepb.Visibility_PUBLIC {
return nil, status.Errorf(codes.PermissionDenied, "Permission denied")
}
user, err := getCurrentUser(ctx, s.Store)
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to get current user: %v", err)
}
if user == nil && collection.Visibility != storepb.Visibility_PUBLIC {
return nil, status.Errorf(codes.PermissionDenied, "Permission denied")
}
return convertCollectionFromStore(collection), nil
}
@ -115,7 +92,7 @@ func (s *APIV1Service) CreateCollection(ctx context.Context, request *v1pb.Creat
user, err := getCurrentUser(ctx, s.Store)
if err != nil {
return nil, status.Errorf(codes.Unauthenticated, "failed to get current user: %v", err)
return nil, status.Errorf(codes.Internal, "failed to get current user: %v", err)
}
collectionCreate := &storepb.Collection{
CreatorId: user.ID,
@ -123,7 +100,7 @@ func (s *APIV1Service) CreateCollection(ctx context.Context, request *v1pb.Creat
Title: request.Collection.Title,
Description: request.Collection.Description,
ShortcutIds: request.Collection.ShortcutIds,
Visibility: storepb.Visibility(request.Collection.Visibility),
Visibility: convertVisibilityToStorepb(request.Collection.Visibility),
}
collection, err := s.Store.CreateCollection(ctx, collectionCreate)
if err != nil {
@ -140,7 +117,7 @@ func (s *APIV1Service) UpdateCollection(ctx context.Context, request *v1pb.Updat
user, err := getCurrentUser(ctx, s.Store)
if err != nil {
return nil, status.Errorf(codes.Unauthenticated, "failed to get current user: %v", err)
return nil, status.Errorf(codes.Internal, "failed to get current user: %v", err)
}
collection, err := s.Store.GetCollection(ctx, &store.FindCollection{
ID: &request.Collection.Id,
@ -169,7 +146,7 @@ func (s *APIV1Service) UpdateCollection(ctx context.Context, request *v1pb.Updat
case "shortcut_ids":
update.ShortcutIDs = request.Collection.ShortcutIds
case "visibility":
visibility := store.Visibility(request.Collection.Visibility.String())
visibility := convertVisibilityToStorepb(request.Collection.Visibility)
update.Visibility = &visibility
}
}
@ -184,7 +161,7 @@ func (s *APIV1Service) UpdateCollection(ctx context.Context, request *v1pb.Updat
func (s *APIV1Service) DeleteCollection(ctx context.Context, request *v1pb.DeleteCollectionRequest) (*emptypb.Empty, error) {
user, err := getCurrentUser(ctx, s.Store)
if err != nil {
return nil, status.Errorf(codes.Unauthenticated, "failed to get current user: %v", err)
return nil, status.Errorf(codes.Internal, "failed to get current user: %v", err)
}
collection, err := s.Store.GetCollection(ctx, &store.FindCollection{
ID: &request.Id,
@ -218,6 +195,6 @@ func convertCollectionFromStore(collection *storepb.Collection) *v1pb.Collection
Title: collection.Title,
Description: collection.Description,
ShortcutIds: collection.ShortcutIds,
Visibility: v1pb.Visibility(collection.Visibility),
Visibility: convertVisibilityFromStorepb(collection.Visibility),
}
}

View File

@ -4,20 +4,10 @@ import (
"context"
v1pb "github.com/yourselfhosted/slash/proto/gen/api/v1"
storepb "github.com/yourselfhosted/slash/proto/gen/store"
"github.com/yourselfhosted/slash/store"
)
func convertRowStatusFromStore(rowStatus store.RowStatus) v1pb.RowStatus {
switch rowStatus {
case store.Normal:
return v1pb.RowStatus_NORMAL
case store.Archived:
return v1pb.RowStatus_ARCHIVED
default:
return v1pb.RowStatus_ROW_STATUS_UNSPECIFIED
}
}
func getCurrentUser(ctx context.Context, s *store.Store) (*store.User, error) {
userID, ok := ctx.Value(userIDContextKey).(int32)
if !ok {
@ -31,3 +21,48 @@ func getCurrentUser(ctx context.Context, s *store.Store) (*store.User, error) {
}
return user, nil
}
func convertStateFromRowStatus(rowStatus storepb.RowStatus) v1pb.State {
switch rowStatus {
case storepb.RowStatus_NORMAL:
return v1pb.State_ACTIVE
case storepb.RowStatus_ARCHIVED:
return v1pb.State_INACTIVE
default:
return v1pb.State_STATE_UNSPECIFIED
}
}
// ConvertStateToRowStatus converts a v1pb.State to a storepb.RowStatus.
func ConvertStateToRowStatus(state v1pb.State) storepb.RowStatus {
switch state {
case v1pb.State_ACTIVE:
return storepb.RowStatus_NORMAL
case v1pb.State_INACTIVE:
return storepb.RowStatus_ARCHIVED
default:
return storepb.RowStatus_ROW_STATUS_UNSPECIFIED
}
}
func convertVisibilityFromStorepb(visibility storepb.Visibility) v1pb.Visibility {
switch visibility {
case storepb.Visibility_WORKSPACE:
return v1pb.Visibility_WORKSPACE
case storepb.Visibility_PUBLIC:
return v1pb.Visibility_PUBLIC
default:
return v1pb.Visibility_VISIBILITY_UNSPECIFIED
}
}
func convertVisibilityToStorepb(visibility v1pb.Visibility) storepb.Visibility {
switch visibility {
case v1pb.Visibility_PRIVATE, v1pb.Visibility_WORKSPACE:
return storepb.Visibility_WORKSPACE
case v1pb.Visibility_PUBLIC:
return storepb.Visibility_PUBLIC
default:
return storepb.Visibility_VISIBILITY_UNSPECIFIED
}
}

View File

@ -22,36 +22,22 @@ import (
)
func (s *APIV1Service) ListShortcuts(ctx context.Context, _ *v1pb.ListShortcutsRequest) (*v1pb.ListShortcutsResponse, error) {
user, err := getCurrentUser(ctx, s.Store)
shortcutList, err := s.Store.ListShortcuts(ctx, &store.FindShortcut{})
if err != nil {
return nil, status.Errorf(codes.Unauthenticated, "failed to get current user: %v", err)
}
find := &store.FindShortcut{}
find.VisibilityList = []store.Visibility{store.VisibilityWorkspace, store.VisibilityPublic}
visibleShortcutList, err := s.Store.ListShortcuts(ctx, find)
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to fetch visible shortcut list, err: %v", err)
return nil, status.Errorf(codes.Internal, "failed to list shortcuts, err: %v", err)
}
find.VisibilityList = []store.Visibility{store.VisibilityPrivate}
find.CreatorID = &user.ID
shortcutList, err := s.Store.ListShortcuts(ctx, find)
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to fetch private shortcut list, err: %v", err)
}
shortcutList = append(shortcutList, visibleShortcutList...)
shortcuts := []*v1pb.Shortcut{}
shortcutMessageList := []*v1pb.Shortcut{}
for _, shortcut := range shortcutList {
composedShortcut, err := s.convertShortcutFromStorepb(ctx, shortcut)
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to convert shortcut, err: %v", err)
}
shortcuts = append(shortcuts, composedShortcut)
shortcutMessageList = append(shortcutMessageList, composedShortcut)
}
response := &v1pb.ListShortcutsResponse{
Shortcuts: shortcuts,
Shortcuts: shortcutMessageList,
}
return response, nil
}
@ -67,15 +53,12 @@ func (s *APIV1Service) GetShortcut(ctx context.Context, request *v1pb.GetShortcu
return nil, status.Errorf(codes.NotFound, "shortcut not found")
}
userID, ok := ctx.Value(userIDContextKey).(int32)
if ok {
if shortcut.Visibility == storepb.Visibility_PRIVATE && shortcut.CreatorId != userID {
return nil, status.Errorf(codes.PermissionDenied, "Permission denied")
}
} else {
if shortcut.Visibility != storepb.Visibility_PUBLIC {
return nil, status.Errorf(codes.PermissionDenied, "Permission denied")
}
user, err := getCurrentUser(ctx, s.Store)
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to get current user: %v", err)
}
if user == nil && shortcut.Visibility != storepb.Visibility_PUBLIC {
return nil, status.Errorf(codes.PermissionDenied, "Permission denied")
}
composedShortcut, err := s.convertShortcutFromStorepb(ctx, shortcut)
@ -96,15 +79,12 @@ func (s *APIV1Service) GetShortcutByName(ctx context.Context, request *v1pb.GetS
return nil, status.Errorf(codes.NotFound, "shortcut not found")
}
userID, ok := ctx.Value(userIDContextKey).(int32)
if ok {
if shortcut.Visibility == storepb.Visibility_PRIVATE && shortcut.CreatorId != userID {
return nil, status.Errorf(codes.PermissionDenied, "Permission denied")
}
} else {
if shortcut.Visibility != storepb.Visibility_PUBLIC {
return nil, status.Errorf(codes.PermissionDenied, "Permission denied")
}
user, err := getCurrentUser(ctx, s.Store)
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to get current user: %v", err)
}
if user == nil && shortcut.Visibility != storepb.Visibility_PUBLIC {
return nil, status.Errorf(codes.PermissionDenied, "Permission denied")
}
composedShortcut, err := s.convertShortcutFromStorepb(ctx, shortcut)
@ -132,7 +112,7 @@ func (s *APIV1Service) CreateShortcut(ctx context.Context, request *v1pb.CreateS
user, err := getCurrentUser(ctx, s.Store)
if err != nil {
return nil, status.Errorf(codes.Unauthenticated, "failed to get current user: %v", err)
return nil, status.Errorf(codes.Internal, "failed to get current user: %v", err)
}
shortcutCreate := &storepb.Shortcut{
CreatorId: user.ID,
@ -141,7 +121,7 @@ func (s *APIV1Service) CreateShortcut(ctx context.Context, request *v1pb.CreateS
Title: request.Shortcut.Title,
Tags: request.Shortcut.Tags,
Description: request.Shortcut.Description,
Visibility: storepb.Visibility(request.Shortcut.Visibility),
Visibility: convertVisibilityToStorepb(request.Shortcut.Visibility),
OgMetadata: &storepb.OpenGraphMetadata{},
}
if shortcutCreate.Visibility == storepb.Visibility_VISIBILITY_UNSPECIFIED {
@ -149,11 +129,11 @@ func (s *APIV1Service) CreateShortcut(ctx context.Context, request *v1pb.CreateS
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to get workspace setting, err: %v", err)
}
visibility := v1pb.Visibility_PRIVATE
visibility := v1pb.Visibility_WORKSPACE
if workspaceSetting.DefaultVisibility != v1pb.Visibility_VISIBILITY_UNSPECIFIED {
visibility = workspaceSetting.DefaultVisibility
}
shortcutCreate.Visibility = storepb.Visibility(visibility)
shortcutCreate.Visibility = convertVisibilityToStorepb(visibility)
}
if request.Shortcut.OgMetadata != nil {
shortcutCreate.OgMetadata = &storepb.OpenGraphMetadata{
@ -184,7 +164,7 @@ func (s *APIV1Service) UpdateShortcut(ctx context.Context, request *v1pb.UpdateS
user, err := getCurrentUser(ctx, s.Store)
if err != nil {
return nil, status.Errorf(codes.Unauthenticated, "failed to get current user: %v", err)
return nil, status.Errorf(codes.Internal, "failed to get current user: %v", err)
}
shortcut, err := s.Store.GetShortcut(ctx, &store.FindShortcut{
ID: &request.Shortcut.Id,
@ -216,7 +196,7 @@ func (s *APIV1Service) UpdateShortcut(ctx context.Context, request *v1pb.UpdateS
tag := strings.Join(request.Shortcut.Tags, " ")
update.Tag = &tag
case "visibility":
visibility := store.Visibility(request.Shortcut.Visibility.String())
visibility := convertVisibilityToStorepb(request.Shortcut.Visibility)
update.Visibility = &visibility
case "og_metadata":
if request.Shortcut.OgMetadata != nil {
@ -243,7 +223,7 @@ func (s *APIV1Service) UpdateShortcut(ctx context.Context, request *v1pb.UpdateS
func (s *APIV1Service) DeleteShortcut(ctx context.Context, request *v1pb.DeleteShortcutRequest) (*emptypb.Empty, error) {
user, err := getCurrentUser(ctx, s.Store)
if err != nil {
return nil, status.Errorf(codes.Unauthenticated, "failed to get current user: %v", err)
return nil, status.Errorf(codes.Internal, "failed to get current user: %v", err)
}
shortcut, err := s.Store.GetShortcut(ctx, &store.FindShortcut{
ID: &request.Id,
@ -369,13 +349,12 @@ func (s *APIV1Service) convertShortcutFromStorepb(ctx context.Context, shortcut
CreatorId: shortcut.CreatorId,
CreatedTime: timestamppb.New(time.Unix(shortcut.CreatedTs, 0)),
UpdatedTime: timestamppb.New(time.Unix(shortcut.UpdatedTs, 0)),
RowStatus: v1pb.RowStatus(shortcut.RowStatus),
Name: shortcut.Name,
Link: shortcut.Link,
Title: shortcut.Title,
Tags: shortcut.Tags,
Description: shortcut.Description,
Visibility: v1pb.Visibility(shortcut.Visibility),
Visibility: convertVisibilityFromStorepb(shortcut.Visibility),
OgMetadata: &v1pb.Shortcut_OpenGraphMetadata{
Title: shortcut.OgMetadata.Title,
Description: shortcut.OgMetadata.Description,

View File

@ -72,7 +72,7 @@ func (s *APIV1Service) CreateUser(ctx context.Context, request *v1pb.CreateUserR
func (s *APIV1Service) UpdateUser(ctx context.Context, request *v1pb.UpdateUserRequest) (*v1pb.User, error) {
user, err := getCurrentUser(ctx, s.Store)
if err != nil {
return nil, status.Errorf(codes.Unauthenticated, "failed to get current user: %v", err)
return nil, status.Errorf(codes.Internal, "failed to get current user: %v", err)
}
if user.ID != request.User.Id {
return nil, status.Errorf(codes.PermissionDenied, "Permission denied")
@ -101,7 +101,7 @@ func (s *APIV1Service) UpdateUser(ctx context.Context, request *v1pb.UpdateUserR
func (s *APIV1Service) DeleteUser(ctx context.Context, request *v1pb.DeleteUserRequest) (*emptypb.Empty, error) {
user, err := getCurrentUser(ctx, s.Store)
if err != nil {
return nil, status.Errorf(codes.Unauthenticated, "failed to get current user: %v", err)
return nil, status.Errorf(codes.Internal, "failed to get current user: %v", err)
}
if user.ID == request.Id {
return nil, status.Errorf(codes.InvalidArgument, "cannot delete yourself")
@ -116,7 +116,7 @@ func (s *APIV1Service) DeleteUser(ctx context.Context, request *v1pb.DeleteUserR
func (s *APIV1Service) ListUserAccessTokens(ctx context.Context, request *v1pb.ListUserAccessTokensRequest) (*v1pb.ListUserAccessTokensResponse, error) {
user, err := getCurrentUser(ctx, s.Store)
if err != nil {
return nil, status.Errorf(codes.Unauthenticated, "failed to get current user: %v", err)
return nil, status.Errorf(codes.Internal, "failed to get current user: %v", err)
}
if user.ID != request.Id {
return nil, status.Errorf(codes.PermissionDenied, "Permission denied")
@ -170,7 +170,7 @@ func (s *APIV1Service) ListUserAccessTokens(ctx context.Context, request *v1pb.L
func (s *APIV1Service) CreateUserAccessToken(ctx context.Context, request *v1pb.CreateUserAccessTokenRequest) (*v1pb.UserAccessToken, error) {
user, err := getCurrentUser(ctx, s.Store)
if err != nil {
return nil, status.Errorf(codes.Unauthenticated, "failed to get current user: %v", err)
return nil, status.Errorf(codes.Internal, "failed to get current user: %v", err)
}
if user.ID != request.Id {
return nil, status.Errorf(codes.PermissionDenied, "Permission denied")
@ -220,7 +220,7 @@ func (s *APIV1Service) CreateUserAccessToken(ctx context.Context, request *v1pb.
func (s *APIV1Service) DeleteUserAccessToken(ctx context.Context, request *v1pb.DeleteUserAccessTokenRequest) (*emptypb.Empty, error) {
user, err := getCurrentUser(ctx, s.Store)
if err != nil {
return nil, status.Errorf(codes.Unauthenticated, "failed to get current user: %v", err)
return nil, status.Errorf(codes.Internal, "failed to get current user: %v", err)
}
userAccessTokens, err := s.Store.GetUserAccessTokens(ctx, user.ID)
if err != nil {
@ -275,7 +275,7 @@ func (s *APIV1Service) UpsertAccessTokenToStore(ctx context.Context, user *store
func convertUserFromStore(user *store.User) *v1pb.User {
return &v1pb.User{
Id: int32(user.ID),
RowStatus: convertRowStatusFromStore(user.RowStatus),
State: convertStateFromRowStatus(user.RowStatus),
CreatedTime: timestamppb.New(time.Unix(user.CreatedTs, 0)),
UpdatedTime: timestamppb.New(time.Unix(user.UpdatedTs, 0)),
Role: convertUserRoleFromStore(user.Role),

View File

@ -27,7 +27,7 @@ func (s *APIV1Service) UpdateUserSetting(ctx context.Context, request *v1pb.Upda
user, err := getCurrentUser(ctx, s.Store)
if err != nil {
return nil, status.Errorf(codes.Unauthenticated, "failed to get current user: %v", err)
return nil, status.Errorf(codes.Internal, "failed to get current user: %v", err)
}
for _, path := range request.UpdateMask.Paths {
if path == "general" {

View File

@ -43,7 +43,7 @@ func (s *APIV1Service) GetWorkspaceProfile(ctx context.Context, _ *v1pb.GetWorks
func (s *APIV1Service) GetWorkspaceSetting(ctx context.Context, _ *v1pb.GetWorkspaceSettingRequest) (*v1pb.WorkspaceSetting, error) {
currentUser, err := getCurrentUser(ctx, s.Store)
if err != nil {
return nil, status.Errorf(codes.Unauthenticated, "failed to get current user: %v", err)
return nil, status.Errorf(codes.Internal, "failed to get current user: %v", err)
}
workspaceSettings, err := s.Store.ListWorkspaceSettings(ctx, &store.FindWorkspaceSetting{})
if err != nil {
@ -61,7 +61,7 @@ func (s *APIV1Service) GetWorkspaceSetting(ctx context.Context, _ *v1pb.GetWorks
workspaceSetting.DisallowPasswordAuth = securitySetting.GetDisallowPasswordAuth()
} else if v.Key == storepb.WorkspaceSettingKey_WORKSPACE_SETTING_SHORTCUT_RELATED {
shortcutRelatedSetting := v.GetShortcutRelated()
workspaceSetting.DefaultVisibility = v1pb.Visibility(shortcutRelatedSetting.GetDefaultVisibility())
workspaceSetting.DefaultVisibility = convertVisibilityFromStorepb(shortcutRelatedSetting.GetDefaultVisibility())
} else if v.Key == storepb.WorkspaceSettingKey_WORKSPACE_SETTING_IDENTITY_PROVIDER {
identityProviderSetting := v.GetIdentityProvider()
workspaceSetting.IdentityProviders = []*v1pb.IdentityProvider{}
@ -129,7 +129,7 @@ func (s *APIV1Service) UpdateWorkspaceSetting(ctx context.Context, request *v1pb
},
}
}
shortcutRelatedSetting.GetShortcutRelated().DefaultVisibility = storepb.Visibility(request.Setting.DefaultVisibility)
shortcutRelatedSetting.GetShortcutRelated().DefaultVisibility = convertVisibilityToStorepb(request.Setting.DefaultVisibility)
if _, err := s.Store.UpsertWorkspaceSetting(ctx, &storepb.WorkspaceSetting{
Key: storepb.WorkspaceSettingKey_WORKSPACE_SETTING_SHORTCUT_RELATED,
Value: &storepb.WorkspaceSetting_ShortcutRelated{