Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
C
canifa_note
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Vũ Hoàng Anh
canifa_note
Commits
76ca258f
Commit
76ca258f
authored
Aug 11, 2025
by
Johnny
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
chore: simplify update user settings
parent
d86756f1
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
38 additions
and
218 deletions
+38
-218
user_service.go
server/router/api/v1/user_service.go
+38
-72
user_service_test.go
server/router/api/v1/user_service_test.go
+0
-146
No files found.
server/router/api/v1/user_service.go
View file @
76ca258f
...
...
@@ -385,11 +385,46 @@ func (s *APIV1Service) UpdateUserSetting(ctx context.Context, request *v1pb.Upda
return
nil
,
status
.
Errorf
(
codes
.
NotFound
,
"%s not found"
,
storeKey
.
String
())
}
// merge only the fields specified by UpdateMask
merged
:=
mergeUserSettingWithMask
(
existingUserSetting
,
request
.
Setting
,
storeKey
,
request
.
UpdateMask
.
Paths
)
// Only GENERAL settings are supported via UpdateUserSetting
// Other setting types have dedicated service methods
if
storeKey
!=
storepb
.
UserSetting_GENERAL
{
return
nil
,
status
.
Errorf
(
codes
.
InvalidArgument
,
"setting type %s should not be updated via UpdateUserSetting"
,
storeKey
.
String
())
}
// Start with existing general setting values
existingGeneral
:=
existingUserSetting
.
GetGeneral
()
updatedGeneral
:=
&
v1pb
.
UserSetting_GeneralSetting
{
Appearance
:
existingGeneral
.
GetAppearance
(),
MemoVisibility
:
existingGeneral
.
GetMemoVisibility
(),
Locale
:
existingGeneral
.
GetLocale
(),
Theme
:
existingGeneral
.
GetTheme
(),
}
// Apply updates for fields specified in the update mask
incomingGeneral
:=
request
.
Setting
.
GetGeneralSetting
()
for
_
,
field
:=
range
request
.
UpdateMask
.
Paths
{
switch
field
{
case
"appearance"
:
updatedGeneral
.
Appearance
=
incomingGeneral
.
Appearance
case
"memoVisibility"
:
updatedGeneral
.
MemoVisibility
=
incomingGeneral
.
MemoVisibility
case
"theme"
:
updatedGeneral
.
Theme
=
incomingGeneral
.
Theme
case
"locale"
:
updatedGeneral
.
Locale
=
incomingGeneral
.
Locale
}
}
// Create the updated setting
updatedSetting
:=
&
v1pb
.
UserSetting
{
Name
:
request
.
Setting
.
Name
,
Value
:
&
v1pb
.
UserSetting_GeneralSetting_
{
GeneralSetting
:
updatedGeneral
,
},
}
// Convert API setting to store setting
storeSetting
,
err
:=
convertUserSettingToStore
(
merged
,
userID
,
storeKey
)
storeSetting
,
err
:=
convertUserSettingToStore
(
updatedSetting
,
userID
,
storeKey
)
if
err
!=
nil
{
return
nil
,
status
.
Errorf
(
codes
.
InvalidArgument
,
"failed to convert setting: %v"
,
err
)
}
...
...
@@ -1335,72 +1370,3 @@ func (s *APIV1Service) validateUserFilter(_ context.Context, filterStr string) e
}
return
nil
}
func
mergeUserSettingWithMask
(
existing
*
storepb
.
UserSetting
,
incoming
*
v1pb
.
UserSetting
,
key
storepb
.
UserSetting_Key
,
paths
[]
string
)
*
v1pb
.
UserSetting
{
if
incoming
==
nil
{
return
&
v1pb
.
UserSetting
{}
}
switch
key
{
case
storepb
.
UserSetting_GENERAL
:
var
gs
*
v1pb
.
UserSetting_GeneralSetting
if
existing
==
nil
{
gs
=
&
v1pb
.
UserSetting_GeneralSetting
{
Locale
:
"en"
,
Appearance
:
"system"
,
MemoVisibility
:
"PRIVATE"
,
Theme
:
""
,
}
}
else
{
gs
=
&
v1pb
.
UserSetting_GeneralSetting
{
Appearance
:
existing
.
GetGeneral
()
.
GetAppearance
(),
MemoVisibility
:
existing
.
GetGeneral
()
.
GetMemoVisibility
(),
Locale
:
existing
.
GetGeneral
()
.
GetLocale
(),
Theme
:
existing
.
GetGeneral
()
.
GetTheme
(),
}
}
for
_
,
field
:=
range
paths
{
switch
field
{
case
"appearance"
:
gs
.
Appearance
=
incoming
.
GetGeneralSetting
()
.
Appearance
case
"memoVisibility"
:
gs
.
MemoVisibility
=
incoming
.
GetGeneralSetting
()
.
MemoVisibility
case
"theme"
:
gs
.
Theme
=
incoming
.
GetGeneralSetting
()
.
Theme
case
"locale"
:
gs
.
Locale
=
incoming
.
GetGeneralSetting
()
.
Locale
}
}
return
&
v1pb
.
UserSetting
{
Name
:
incoming
.
Name
,
Value
:
&
v1pb
.
UserSetting_GeneralSetting_
{
GeneralSetting
:
gs
,
},
}
case
storepb
.
UserSetting_SHORTCUTS
:
// handled by the FE calling shortcut_service.CreateShortcut
// if the FE wants to modify shortcuts by calling the user_service we need to handle below
return
incoming
case
storepb
.
UserSetting_WEBHOOKS
:
// handled by the FE calling user_service.CreateUserWebhook
// if the FE wants to modify webhooks by calling the user_service we need to handle below
return
incoming
case
storepb
.
UserSetting_ACCESS_TOKENS
:
// handled by the FE calling user_service.CreateUserAccessToken
// if the FE wants to modify access tokens by calling the user_service we need to handle below
return
incoming
case
storepb
.
UserSetting_SESSIONS
:
// handled by the FE calling auth_service.CreateSession
// if the FE wants to modify sessions by calling the user_service we need to handle below
return
incoming
default
:
return
incoming
}
}
server/router/api/v1/user_service_test.go
deleted
100644 → 0
View file @
d86756f1
package
v1
import
(
"reflect"
"testing"
v1pb
"github.com/usememos/memos/proto/gen/api/v1"
storepb
"github.com/usememos/memos/proto/gen/store"
)
func
TestMergeUserSettingWithMask
(
t
*
testing
.
T
)
{
tests
:=
[]
struct
{
name
string
existing
*
storepb
.
UserSetting
incoming
*
v1pb
.
UserSetting
key
storepb
.
UserSetting_Key
paths
[]
string
expected
*
v1pb
.
UserSetting
}{
{
name
:
"adds new field without removing existing fields"
,
existing
:
&
storepb
.
UserSetting
{
UserId
:
1
,
Key
:
storepb
.
UserSetting_GENERAL
,
Value
:
&
storepb
.
UserSetting_General
{
General
:
&
storepb
.
GeneralUserSetting
{
MemoVisibility
:
"PROTECTED"
,
},
},
},
incoming
:
&
v1pb
.
UserSetting
{
Value
:
&
v1pb
.
UserSetting_GeneralSetting_
{
GeneralSetting
:
&
v1pb
.
UserSetting_GeneralSetting
{
Appearance
:
"light"
,
},
},
},
key
:
storepb
.
UserSetting_GENERAL
,
paths
:
[]
string
{
"appearance"
},
expected
:
&
v1pb
.
UserSetting
{
Value
:
&
v1pb
.
UserSetting_GeneralSetting_
{
GeneralSetting
:
&
v1pb
.
UserSetting_GeneralSetting
{
Appearance
:
"light"
,
MemoVisibility
:
"PROTECTED"
,
},
},
},
},
{
name
:
"adds new field when no existing fields exist"
,
existing
:
&
storepb
.
UserSetting
{
UserId
:
1
,
Key
:
storepb
.
UserSetting_GENERAL
,
Value
:
&
storepb
.
UserSetting_General
{},
},
incoming
:
&
v1pb
.
UserSetting
{
Value
:
&
v1pb
.
UserSetting_GeneralSetting_
{
GeneralSetting
:
&
v1pb
.
UserSetting_GeneralSetting
{
Theme
:
"whitewall"
,
},
},
},
key
:
storepb
.
UserSetting_GENERAL
,
paths
:
[]
string
{
"theme"
},
expected
:
&
v1pb
.
UserSetting
{
Value
:
&
v1pb
.
UserSetting_GeneralSetting_
{
GeneralSetting
:
&
v1pb
.
UserSetting_GeneralSetting
{
Theme
:
"whitewall"
,
},
},
},
},
{
name
:
"updates existing field without removing existing fields"
,
existing
:
&
storepb
.
UserSetting
{
UserId
:
1
,
Key
:
storepb
.
UserSetting_GENERAL
,
Value
:
&
storepb
.
UserSetting_General
{
General
:
&
storepb
.
GeneralUserSetting
{
Appearance
:
"dark"
,
MemoVisibility
:
"PUBLIC"
,
},
},
},
incoming
:
&
v1pb
.
UserSetting
{
Value
:
&
v1pb
.
UserSetting_GeneralSetting_
{
GeneralSetting
:
&
v1pb
.
UserSetting_GeneralSetting
{
Appearance
:
"light"
,
},
},
},
key
:
storepb
.
UserSetting_GENERAL
,
paths
:
[]
string
{
"appearance"
},
expected
:
&
v1pb
.
UserSetting
{
Value
:
&
v1pb
.
UserSetting_GeneralSetting_
{
GeneralSetting
:
&
v1pb
.
UserSetting_GeneralSetting
{
Appearance
:
"light"
,
MemoVisibility
:
"PUBLIC"
,
},
},
},
},
{
name
:
"updates multiple fields without removing existing fields"
,
existing
:
&
storepb
.
UserSetting
{
UserId
:
1
,
Key
:
storepb
.
UserSetting_GENERAL
,
Value
:
&
storepb
.
UserSetting_General
{
General
:
&
storepb
.
GeneralUserSetting
{
Appearance
:
"system"
,
},
},
},
incoming
:
&
v1pb
.
UserSetting
{
Value
:
&
v1pb
.
UserSetting_GeneralSetting_
{
GeneralSetting
:
&
v1pb
.
UserSetting_GeneralSetting
{
Appearance
:
"dark"
,
Theme
:
"paper"
,
MemoVisibility
:
"PROTECTED"
,
},
},
},
key
:
storepb
.
UserSetting_GENERAL
,
paths
:
[]
string
{
"theme"
,
"memoVisibility"
,
"appearance"
},
expected
:
&
v1pb
.
UserSetting
{
Value
:
&
v1pb
.
UserSetting_GeneralSetting_
{
GeneralSetting
:
&
v1pb
.
UserSetting_GeneralSetting
{
Appearance
:
"dark"
,
MemoVisibility
:
"PROTECTED"
,
Theme
:
"paper"
,
},
},
},
},
}
for
_
,
tt
:=
range
tests
{
t
.
Run
(
tt
.
name
,
func
(
t
*
testing
.
T
)
{
actual
:=
mergeUserSettingWithMask
(
tt
.
existing
,
tt
.
incoming
,
tt
.
key
,
tt
.
paths
)
if
!
reflect
.
DeepEqual
(
actual
,
tt
.
expected
)
{
t
.
Errorf
(
"expected %v but got %v"
,
tt
.
expected
,
actual
)
}
})
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment