From aa247ccef27521a51abbc0c1f467a4320c26ef1e Mon Sep 17 00:00:00 2001 From: Steven Date: Tue, 12 Sep 2023 21:02:33 +0800 Subject: [PATCH] chore: impl workspace setting service --- api/v2/v2.go | 4 + api/v2/workspace_setting_service.go | 82 +++++++++++++++++ store/workspace_setting_v1.go | 137 ++++++++++++++++++++++++++++ 3 files changed, 223 insertions(+) create mode 100644 api/v2/workspace_setting_service.go create mode 100644 store/workspace_setting_v1.go diff --git a/api/v2/v2.go b/api/v2/v2.go index b7127533..47b335db 100644 --- a/api/v2/v2.go +++ b/api/v2/v2.go @@ -29,6 +29,7 @@ func NewAPIV2Service(secret string, profile *profile.Profile, store *store.Store authProvider.AuthenticationInterceptor, ), ) + apiv2pb.RegisterWorkspaceSettingServiceServer(grpcServer, NewWorkspaceSettingService(store)) apiv2pb.RegisterUserServiceServer(grpcServer, NewUserService(secret, store)) apiv2pb.RegisterUserSettingServiceServer(grpcServer, NewUserSettingService(store)) apiv2pb.RegisterShortcutServiceServer(grpcServer, NewShortcutService(secret, store)) @@ -60,6 +61,9 @@ func (s *APIV2Service) RegisterGateway(ctx context.Context, e *echo.Echo) error } gwMux := grpcRuntime.NewServeMux() + if err := apiv2pb.RegisterWorkspaceSettingServiceHandler(context.Background(), gwMux, conn); err != nil { + return err + } if err := apiv2pb.RegisterUserServiceHandler(context.Background(), gwMux, conn); err != nil { return err } diff --git a/api/v2/workspace_setting_service.go b/api/v2/workspace_setting_service.go new file mode 100644 index 00000000..5aa1ed50 --- /dev/null +++ b/api/v2/workspace_setting_service.go @@ -0,0 +1,82 @@ +package v2 + +import ( + "context" + + apiv2pb "github.com/boojack/slash/proto/gen/api/v2" + storepb "github.com/boojack/slash/proto/gen/store" + "github.com/boojack/slash/store" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +type WorkspaceSettingService struct { + apiv2pb.UnimplementedWorkspaceSettingServiceServer + + Store *store.Store +} + +// NewWorkspaceSettingService creates a new WorkspaceSettingService. +func NewWorkspaceSettingService(store *store.Store) *WorkspaceSettingService { + return &WorkspaceSettingService{ + Store: store, + } +} + +func (s *WorkspaceSettingService) GetWorkspaceSetting(ctx context.Context, _ *apiv2pb.GetWorkspaceSettingRequest) (*apiv2pb.GetWorkspaceSettingResponse, error) { + workspaceSettings, err := s.Store.ListWorkspaceSettingsV1(ctx, &store.FindWorkspaceSettingV1{}) + if err != nil { + return nil, status.Errorf(codes.Internal, "failed to list workspace settings: %v", err) + } + workspaceSetting := &apiv2pb.WorkspaceSetting{} + for _, v := range workspaceSettings { + if v.Key == storepb.WorkspaceSettingKey_WORKSAPCE_SETTING_ENABLE_SIGNUP { + workspaceSetting.EnableSignup = v.GetEnableSignup() + } else if v.Key == storepb.WorkspaceSettingKey_WORKSPACE_SETTING_RESOURCE_RELATIVE_PATH { + workspaceSetting.ResourceRelativePath = v.GetResourceRelativePath() + } else { + return nil, status.Errorf(codes.Internal, "invalid workspace setting key: %s", v.Key.String()) + } + } + return &apiv2pb.GetWorkspaceSettingResponse{ + Setting: workspaceSetting, + }, nil +} + +func (s *WorkspaceSettingService) UpdateWorkspaceSetting(ctx context.Context, request *apiv2pb.UpdateWorkspaceSettingRequest) (*apiv2pb.UpdateWorkspaceSettingResponse, error) { + if request.UpdateMask == nil || len(request.UpdateMask) == 0 { + return nil, status.Errorf(codes.InvalidArgument, "update mask is empty") + } + + for _, path := range request.UpdateMask { + if path == "enable_signup" { + if _, err := s.Store.UpsertWorkspaceSettingV1(ctx, &storepb.WorkspaceSetting{ + Key: storepb.WorkspaceSettingKey_WORKSAPCE_SETTING_ENABLE_SIGNUP, + Value: &storepb.WorkspaceSetting_EnableSignup{ + EnableSignup: request.Setting.EnableSignup, + }, + }); err != nil { + return nil, status.Errorf(codes.Internal, "failed to update workspace setting: %v", err) + } + } else if path == "resource_relative_path" { + if _, err := s.Store.UpsertWorkspaceSettingV1(ctx, &storepb.WorkspaceSetting{ + Key: storepb.WorkspaceSettingKey_WORKSPACE_SETTING_RESOURCE_RELATIVE_PATH, + Value: &storepb.WorkspaceSetting_ResourceRelativePath{ + ResourceRelativePath: request.Setting.ResourceRelativePath, + }, + }); err != nil { + return nil, status.Errorf(codes.Internal, "failed to update workspace setting: %v", err) + } + } else { + return nil, status.Errorf(codes.InvalidArgument, "invalid path: %s", path) + } + } + + getWorkspaceSettingResponse, err := s.GetWorkspaceSetting(ctx, &apiv2pb.GetWorkspaceSettingRequest{}) + if err != nil { + return nil, status.Errorf(codes.Internal, "failed to get workspace setting: %v", err) + } + return &apiv2pb.UpdateWorkspaceSettingResponse{ + Setting: getWorkspaceSettingResponse.Setting, + }, nil +} diff --git a/store/workspace_setting_v1.go b/store/workspace_setting_v1.go new file mode 100644 index 00000000..b3175dfe --- /dev/null +++ b/store/workspace_setting_v1.go @@ -0,0 +1,137 @@ +package store + +import ( + "context" + "errors" + "strconv" + "strings" + + storepb "github.com/boojack/slash/proto/gen/store" + "google.golang.org/protobuf/encoding/protojson" +) + +type FindWorkspaceSettingV1 struct { + Key storepb.WorkspaceSettingKey +} + +func (s *Store) UpsertWorkspaceSettingV1(ctx context.Context, upsert *storepb.WorkspaceSetting) (*storepb.WorkspaceSetting, error) { + stmt := ` + INSERT INTO workspace_setting ( + key, + value + ) + VALUES (?, ?) + ON CONFLICT(key) DO UPDATE + SET value = EXCLUDED.value + ` + var valueString string + if upsert.Key == storepb.WorkspaceSettingKey_WORKSPACE_SETTING_SECRET_SESSION { + valueString = upsert.GetSecretSession() + } else if upsert.Key == storepb.WorkspaceSettingKey_WORKSAPCE_SETTING_ENABLE_SIGNUP { + valueString = strconv.FormatBool(upsert.GetEnableSignup()) + } else if upsert.Key == storepb.WorkspaceSettingKey_WORKSPACE_SETTING_RESOURCE_RELATIVE_PATH { + valueString = upsert.GetResourceRelativePath() + } else if upsert.Key == storepb.WorkspaceSettingKey_WORKSPACE_SETTING_AUTO_BACKUP { + valueBytes, err := protojson.Marshal(upsert.GetAutoBackup()) + if err != nil { + return nil, err + } + valueString = string(valueBytes) + } else { + return nil, errors.New("invalid workspace setting key") + } + + if _, err := s.db.ExecContext(ctx, stmt, upsert.Key.String(), valueString); err != nil { + return nil, err + } + + workspaceSetting := upsert + s.workspaceSettingCache.Store(workspaceSetting.Key, workspaceSetting) + return workspaceSetting, nil +} + +func (s *Store) ListWorkspaceSettingsV1(ctx context.Context, find *FindWorkspaceSettingV1) ([]*storepb.WorkspaceSetting, error) { + where, args := []string{"1 = 1"}, []any{} + + if find.Key != storepb.WorkspaceSettingKey_WORKSPACE_SETTING_KEY_UNSPECIFIED { + where, args = append(where, "key = ?"), append(args, find.Key.String()) + } + + query := ` + SELECT + key, + value + FROM workspace_setting + WHERE ` + strings.Join(where, " AND ") + rows, err := s.db.QueryContext(ctx, query, args...) + if err != nil { + return nil, err + } + + defer rows.Close() + + list := []*storepb.WorkspaceSetting{} + for rows.Next() { + workspaceSetting := &storepb.WorkspaceSetting{} + var keyString, valueString string + if err := rows.Scan( + &keyString, + &valueString, + ); err != nil { + return nil, err + } + workspaceSetting.Key = storepb.WorkspaceSettingKey(storepb.WorkspaceSettingKey_value[keyString]) + if workspaceSetting.Key == storepb.WorkspaceSettingKey_WORKSPACE_SETTING_SECRET_SESSION { + workspaceSetting.Value = &storepb.WorkspaceSetting_SecretSession{SecretSession: valueString} + } else if workspaceSetting.Key == storepb.WorkspaceSettingKey_WORKSAPCE_SETTING_ENABLE_SIGNUP { + enableSignup, err := strconv.ParseBool(valueString) + if err != nil { + return nil, err + } + workspaceSetting.Value = &storepb.WorkspaceSetting_EnableSignup{EnableSignup: enableSignup} + } else if workspaceSetting.Key == storepb.WorkspaceSettingKey_WORKSPACE_SETTING_RESOURCE_RELATIVE_PATH { + workspaceSetting.Value = &storepb.WorkspaceSetting_ResourceRelativePath{ResourceRelativePath: valueString} + } else if workspaceSetting.Key == storepb.WorkspaceSettingKey_WORKSPACE_SETTING_AUTO_BACKUP { + autoBackupSetting := &storepb.AutoBackupWorkspaceSetting{} + if err := protojson.Unmarshal([]byte(valueString), autoBackupSetting); err != nil { + return nil, err + } + workspaceSetting.Value = &storepb.WorkspaceSetting_AutoBackup{AutoBackup: autoBackupSetting} + } else { + return nil, errors.New("invalid workspace setting key") + } + + list = append(list, workspaceSetting) + } + + if err := rows.Err(); err != nil { + return nil, err + } + + for _, workspaceSetting := range list { + s.workspaceSettingCache.Store(workspaceSetting.Key, workspaceSetting) + } + + return list, nil +} + +func (s *Store) GetWorkspaceSettingV1(ctx context.Context, find *FindWorkspaceSettingV1) (*storepb.WorkspaceSetting, error) { + if find.Key != storepb.WorkspaceSettingKey_WORKSAPCE_SETTING_ENABLE_SIGNUP { + if cache, ok := s.workspaceSettingCache.Load(find.Key); ok { + return cache.(*storepb.WorkspaceSetting), nil + } + } + + list, err := s.ListWorkspaceSettingsV1(ctx, find) + if err != nil { + return nil, err + } + + if len(list) == 0 { + return nil, nil + } + + workspaceSetting := list[0] + s.workspaceSettingCache.Store(workspaceSetting.Key, workspaceSetting) + return workspaceSetting, nil +}