1
0
mirror of https://github.com/mattermost/focalboard.git synced 2025-07-12 23:50:27 +02:00

Update user store to return the updated entities after inserting / patching them (#3843)

This commit is contained in:
Miguel de la Cruz
2022-09-15 13:58:20 +02:00
committed by GitHub
parent f574286e6e
commit dfa9d8f452
16 changed files with 163 additions and 158 deletions

View File

@ -115,7 +115,6 @@ func (a *API) handleGetMe(w http.ResponseWriter, r *http.Request) {
Email: model.SingleUser, Email: model.SingleUser,
CreateAt: ws.UpdateAt, CreateAt: ws.UpdateAt,
UpdateAt: now, UpdateAt: now,
Props: map[string]interface{}{},
} }
} else { } else {
user, err = a.app.GetUser(userID) user, err = a.app.GetUser(userID)
@ -280,7 +279,7 @@ func (a *API) handleUpdateUserConfig(w http.ResponseWriter, r *http.Request) {
return return
} }
var patch *model.UserPropPatch var patch *model.UserPreferencesPatch
err = json.Unmarshal(requestBody, &patch) err = json.Unmarshal(requestBody, &patch)
if err != nil { if err != nil {
a.errorResponse(w, r, err) a.errorResponse(w, r, err)

View File

@ -178,7 +178,7 @@ func (a *App) RegisterUser(username, email, password string) error {
return errors.Wrap(err, "Invalid password") return errors.Wrap(err, "Invalid password")
} }
err = a.store.CreateUser(&model.User{ _, err = a.store.CreateUser(&model.User{
ID: utils.NewID(utils.IDTypeUser), ID: utils.NewID(utils.IDTypeUser),
Username: username, Username: username,
Email: email, Email: email,
@ -186,7 +186,6 @@ func (a *App) RegisterUser(username, email, password string) error {
MfaSecret: "", MfaSecret: "",
AuthService: a.config.AuthMode, AuthService: a.config.AuthMode,
AuthData: "", AuthData: "",
Props: map[string]interface{}{},
}) })
if err != nil { if err != nil {
return errors.Wrap(err, "Unable to create the new user") return errors.Wrap(err, "Unable to create the new user")

View File

@ -109,7 +109,7 @@ func TestRegisterUser(t *testing.T) {
th.Store.EXPECT().GetUserByUsername("newUsername").Return(mockUser, errors.New("user not found")) th.Store.EXPECT().GetUserByUsername("newUsername").Return(mockUser, errors.New("user not found"))
th.Store.EXPECT().GetUserByEmail("existingEmail").Return(mockUser, nil) th.Store.EXPECT().GetUserByEmail("existingEmail").Return(mockUser, nil)
th.Store.EXPECT().GetUserByEmail("newEmail").Return(nil, model.NewErrNotFound("user")) th.Store.EXPECT().GetUserByEmail("newEmail").Return(nil, model.NewErrNotFound("user"))
th.Store.EXPECT().CreateUser(gomock.Any()).Return(nil) th.Store.EXPECT().CreateUser(gomock.Any()).Return(nil, nil)
for _, test := range testcases { for _, test := range testcases {
t.Run(test.title, func(t *testing.T) { t.Run(test.title, func(t *testing.T) {

View File

@ -30,14 +30,14 @@ func (a *App) PrepareOnboardingTour(userID string, teamID string) (string, strin
} }
// set user's tour state to initial state // set user's tour state to initial state
userPropPatch := model.UserPropPatch{ userPreferencesPatch := model.UserPreferencesPatch{
UpdatedFields: map[string]string{ UpdatedFields: map[string]string{
KeyOnboardingTourStarted: "1", KeyOnboardingTourStarted: "1",
KeyOnboardingTourStep: ValueOnboardingFirstStep, KeyOnboardingTourStep: ValueOnboardingFirstStep,
KeyOnboardingTourCategory: ValueTourCategoryOnboarding, KeyOnboardingTourCategory: ValueTourCategoryOnboarding,
}, },
} }
if err := a.store.PatchUserProps(userID, userPropPatch); err != nil { if _, err := a.store.PatchUserPreferences(userID, userPreferencesPatch); err != nil {
return "", "", err return "", "", err
} }

View File

@ -42,7 +42,7 @@ func TestPrepareOnboardingTour(t *testing.T) {
newType := model.BoardTypePrivate newType := model.BoardTypePrivate
th.Store.EXPECT().PatchBoard("board_id_1", &model.BoardPatch{Type: &newType}, "user_id_1").Return(&privateWelcomeBoard, nil) th.Store.EXPECT().PatchBoard("board_id_1", &model.BoardPatch{Type: &newType}, "user_id_1").Return(&privateWelcomeBoard, nil)
userPropPatch := model.UserPropPatch{ userPreferencesPatch := model.UserPreferencesPatch{
UpdatedFields: map[string]string{ UpdatedFields: map[string]string{
KeyOnboardingTourStarted: "1", KeyOnboardingTourStarted: "1",
KeyOnboardingTourStep: ValueOnboardingFirstStep, KeyOnboardingTourStep: ValueOnboardingFirstStep,
@ -50,7 +50,7 @@ func TestPrepareOnboardingTour(t *testing.T) {
}, },
} }
th.Store.EXPECT().PatchUserProps(userID, userPropPatch).Return(nil) th.Store.EXPECT().PatchUserPreferences(userID, userPreferencesPatch).Return(nil, nil)
th.Store.EXPECT().GetUserCategoryBoards(userID, "0").Return([]model.CategoryBoards{}, nil) th.Store.EXPECT().GetUserCategoryBoards(userID, "0").Return([]model.CategoryBoards{}, nil)
teamID, boardID, err := th.App.PrepareOnboardingTour(userID, teamID) teamID, boardID, err := th.App.PrepareOnboardingTour(userID, teamID)

View File

@ -13,12 +13,8 @@ func (a *App) SearchTeamUsers(teamID string, searchQuery string, asGuestID strin
return a.store.SearchUsersByTeam(teamID, searchQuery, asGuestID, excludeBots) return a.store.SearchUsersByTeam(teamID, searchQuery, asGuestID, excludeBots)
} }
func (a *App) UpdateUserConfig(userID string, patch model.UserPropPatch) ([]mmModel.Preference, error) { func (a *App) UpdateUserConfig(userID string, patch model.UserPreferencesPatch) ([]mmModel.Preference, error) {
if err := a.store.PatchUserProps(userID, patch); err != nil { updatedPreferences, err := a.store.PatchUserPreferences(userID, patch)
return nil, err
}
updatedPreferences, err := a.store.GetUserPreferences(userID)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -2561,7 +2561,7 @@ func TestPermissionsUserChangePassword(t *testing.T) {
} }
func TestPermissionsUpdateUserConfig(t *testing.T) { func TestPermissionsUpdateUserConfig(t *testing.T) {
patch := toJSON(t, model.UserPropPatch{UpdatedFields: map[string]string{"test": "test"}}) patch := toJSON(t, model.UserPreferencesPatch{UpdatedFields: map[string]string{"test": "test"}})
ttCases := []TestCase{ ttCases := []TestCase{
{"/users/{USER_TEAM_MEMBER_ID}/config", methodPut, patch, userAnon, http.StatusUnauthorized, 0}, {"/users/{USER_TEAM_MEMBER_ID}/config", methodPut, patch, userAnon, http.StatusUnauthorized, 0},

View File

@ -27,7 +27,6 @@ func NewPluginTestStore(innerStore store.Store) *PluginTestStore {
users: map[string]*model.User{ users: map[string]*model.User{
"no-team-member": { "no-team-member": {
ID: "no-team-member", ID: "no-team-member",
Props: map[string]interface{}{},
Username: "no-team-member", Username: "no-team-member",
Email: "no-team-member@sample.com", Email: "no-team-member@sample.com",
CreateAt: model.GetMillis(), CreateAt: model.GetMillis(),
@ -35,7 +34,6 @@ func NewPluginTestStore(innerStore store.Store) *PluginTestStore {
}, },
"team-member": { "team-member": {
ID: "team-member", ID: "team-member",
Props: map[string]interface{}{},
Username: "team-member", Username: "team-member",
Email: "team-member@sample.com", Email: "team-member@sample.com",
CreateAt: model.GetMillis(), CreateAt: model.GetMillis(),
@ -43,7 +41,6 @@ func NewPluginTestStore(innerStore store.Store) *PluginTestStore {
}, },
"viewer": { "viewer": {
ID: "viewer", ID: "viewer",
Props: map[string]interface{}{},
Username: "viewer", Username: "viewer",
Email: "viewer@sample.com", Email: "viewer@sample.com",
CreateAt: model.GetMillis(), CreateAt: model.GetMillis(),
@ -51,7 +48,6 @@ func NewPluginTestStore(innerStore store.Store) *PluginTestStore {
}, },
"commenter": { "commenter": {
ID: "commenter", ID: "commenter",
Props: map[string]interface{}{},
Username: "commenter", Username: "commenter",
Email: "commenter@sample.com", Email: "commenter@sample.com",
CreateAt: model.GetMillis(), CreateAt: model.GetMillis(),
@ -59,7 +55,6 @@ func NewPluginTestStore(innerStore store.Store) *PluginTestStore {
}, },
"editor": { "editor": {
ID: "editor", ID: "editor",
Props: map[string]interface{}{},
Username: "editor", Username: "editor",
Email: "editor@sample.com", Email: "editor@sample.com",
CreateAt: model.GetMillis(), CreateAt: model.GetMillis(),
@ -67,7 +62,6 @@ func NewPluginTestStore(innerStore store.Store) *PluginTestStore {
}, },
"admin": { "admin": {
ID: "admin", ID: "admin",
Props: map[string]interface{}{},
Username: "admin", Username: "admin",
Email: "admin@sample.com", Email: "admin@sample.com",
CreateAt: model.GetMillis(), CreateAt: model.GetMillis(),
@ -75,7 +69,6 @@ func NewPluginTestStore(innerStore store.Store) *PluginTestStore {
}, },
"guest": { "guest": {
ID: "guest", ID: "guest",
Props: map[string]interface{}{},
Username: "guest", Username: "guest",
Email: "guest@sample.com", Email: "guest@sample.com",
CreateAt: model.GetMillis(), CreateAt: model.GetMillis(),
@ -150,27 +143,6 @@ func (s *PluginTestStore) GetUserByUsername(username string) (*model.User, error
return nil, errTestStore return nil, errTestStore
} }
func (s *PluginTestStore) PatchUserProps(userID string, patch model.UserPropPatch) error {
user, err := s.GetUserByID(userID)
if err != nil {
return err
}
props := user.Props
for _, key := range patch.DeletedFields {
delete(props, key)
}
for key, value := range patch.UpdatedFields {
props[key] = value
}
user.Props = props
return nil
}
func (s *PluginTestStore) GetUserPreferences(userID string) (mmModel.Preferences, error) { func (s *PluginTestStore) GetUserPreferences(userID string) (mmModel.Preferences, error) {
if userID == userTeamMember { if userID == userTeamMember {
return mmModel.Preferences{{ return mmModel.Preferences{{

View File

@ -46,10 +46,6 @@ type User struct {
// swagger:ignore // swagger:ignore
AuthData string `json:"-"` AuthData string `json:"-"`
// User settings
// required: true
Props map[string]interface{} `json:"props"`
// Created time in miliseconds since the current epoch // Created time in miliseconds since the current epoch
// required: true // required: true
CreateAt int64 `json:"create_at,omitempty"` CreateAt int64 `json:"create_at,omitempty"`
@ -73,14 +69,14 @@ type User struct {
Roles string `json:"roles"` Roles string `json:"roles"`
} }
// UserPropPatch is a user property patch // UserPreferencesPatch is a user property patch
// swagger:model // swagger:model
type UserPropPatch struct { type UserPreferencesPatch struct {
// The user prop updated fields // The user preference updated fields
// required: false // required: false
UpdatedFields map[string]string `json:"updatedFields"` UpdatedFields map[string]string `json:"updatedFields"`
// The user prop removed fields // The user preference removed fields
// required: false // required: false
DeletedFields []string `json:"deletedFields"` DeletedFields []string `json:"deletedFields"`
} }

View File

@ -115,12 +115,12 @@ func (s *MattermostAuthLayer) GetUserByUsername(username string) (*model.User, e
return &user, nil return &user, nil
} }
func (s *MattermostAuthLayer) CreateUser(user *model.User) error { func (s *MattermostAuthLayer) CreateUser(user *model.User) (*model.User, error) {
return store.NewNotSupportedError("no user creation allowed from focalboard, create it using mattermost") return nil, store.NewNotSupportedError("no user creation allowed from focalboard, create it using mattermost")
} }
func (s *MattermostAuthLayer) UpdateUser(user *model.User) error { func (s *MattermostAuthLayer) UpdateUser(user *model.User) (*model.User, error) {
return store.NewNotSupportedError("no update allowed from focalboard, update it using mattermost") return nil, store.NewNotSupportedError("no update allowed from focalboard, update it using mattermost")
} }
func (s *MattermostAuthLayer) UpdateUserPassword(username, password string) error { func (s *MattermostAuthLayer) UpdateUserPassword(username, password string) error {
@ -131,7 +131,12 @@ func (s *MattermostAuthLayer) UpdateUserPasswordByID(userID, password string) er
return store.NewNotSupportedError("no update allowed from focalboard, update it using mattermost") return store.NewNotSupportedError("no update allowed from focalboard, update it using mattermost")
} }
func (s *MattermostAuthLayer) PatchUserProps(userID string, patch model.UserPropPatch) error { func (s *MattermostAuthLayer) PatchUserPreferences(userID string, patch model.UserPreferencesPatch) (mmModel.Preferences, error) {
preferences, err := s.GetUserPreferences(userID)
if err != nil {
return nil, err
}
if len(patch.UpdatedFields) > 0 { if len(patch.UpdatedFields) > 0 {
updatedPreferences := mmModel.Preferences{} updatedPreferences := mmModel.Preferences{}
for key, value := range patch.UpdatedFields { for key, value := range patch.UpdatedFields {
@ -147,8 +152,27 @@ func (s *MattermostAuthLayer) PatchUserProps(userID string, patch model.UserProp
if err := s.servicesAPI.UpdatePreferencesForUser(userID, updatedPreferences); err != nil { if err := s.servicesAPI.UpdatePreferencesForUser(userID, updatedPreferences); err != nil {
s.logger.Error("failed to update user preferences", mlog.String("user_id", userID), mlog.Err(err)) s.logger.Error("failed to update user preferences", mlog.String("user_id", userID), mlog.Err(err))
return err return nil, err
} }
// we update the preferences list replacing or adding those
// that were updated
newPreferences := mmModel.Preferences{}
for _, existingPreference := range preferences {
hasBeenUpdated := false
for _, updatedPreference := range updatedPreferences {
if updatedPreference.Name == existingPreference.Name {
hasBeenUpdated = true
break
}
}
if !hasBeenUpdated {
newPreferences = append(newPreferences, existingPreference)
}
}
newPreferences = append(newPreferences, updatedPreferences...)
preferences = newPreferences
} }
if len(patch.DeletedFields) > 0 { if len(patch.DeletedFields) > 0 {
@ -165,11 +189,29 @@ func (s *MattermostAuthLayer) PatchUserProps(userID string, patch model.UserProp
if err := s.servicesAPI.DeletePreferencesForUser(userID, deletedPreferences); err != nil { if err := s.servicesAPI.DeletePreferencesForUser(userID, deletedPreferences); err != nil {
s.logger.Error("failed to delete user preferences", mlog.String("user_id", userID), mlog.Err(err)) s.logger.Error("failed to delete user preferences", mlog.String("user_id", userID), mlog.Err(err))
return err return nil, err
} }
// we update the preferences removing those that have been
// deleted
newPreferences := mmModel.Preferences{}
for _, existingPreference := range preferences {
hasBeenDeleted := false
for _, deletedPreference := range deletedPreferences {
if deletedPreference.Name == existingPreference.Name {
hasBeenDeleted = true
break
}
}
if !hasBeenDeleted {
newPreferences = append(newPreferences, existingPreference)
}
}
preferences = newPreferences
} }
return nil return preferences, nil
} }
func (s *MattermostAuthLayer) GetUserPreferences(userID string) (mmModel.Preferences, error) { func (s *MattermostAuthLayer) GetUserPreferences(userID string) (mmModel.Preferences, error) {
@ -291,7 +333,7 @@ func (s *MattermostAuthLayer) getQueryBuilder() sq.StatementBuilderType {
func (s *MattermostAuthLayer) GetUsersByTeam(teamID string, asGuestID string) ([]*model.User, error) { func (s *MattermostAuthLayer) GetUsersByTeam(teamID string, asGuestID string) ([]*model.User, error) {
query := s.getQueryBuilder(). query := s.getQueryBuilder().
Select("u.id", "u.username", "u.email", "u.nickname", "u.firstname", "u.lastname", "u.props", "u.CreateAt as create_at", "u.UpdateAt as update_at", Select("u.id", "u.username", "u.email", "u.nickname", "u.firstname", "u.lastname", "u.CreateAt as create_at", "u.UpdateAt as update_at",
"u.DeleteAt as delete_at", "b.UserId IS NOT NULL AS is_bot, u.roles = 'system_guest' as is_guest"). "u.DeleteAt as delete_at", "b.UserId IS NOT NULL AS is_bot, u.roles = 'system_guest' as is_guest").
From("Users as u"). From("Users as u").
LeftJoin("Bots b ON ( b.UserID = u.id )"). LeftJoin("Bots b ON ( b.UserID = u.id )").
@ -332,7 +374,7 @@ func (s *MattermostAuthLayer) GetUsersByTeam(teamID string, asGuestID string) ([
func (s *MattermostAuthLayer) GetUsersList(userIDs []string) ([]*model.User, error) { func (s *MattermostAuthLayer) GetUsersList(userIDs []string) ([]*model.User, error) {
query := s.getQueryBuilder(). query := s.getQueryBuilder().
Select("u.id", "u.username", "u.email", "u.nickname", "u.firstname", "u.lastname", "u.props", "u.CreateAt as create_at", "u.UpdateAt as update_at", Select("u.id", "u.username", "u.email", "u.nickname", "u.firstname", "u.lastname", "u.CreateAt as create_at", "u.UpdateAt as update_at",
"u.DeleteAt as delete_at", "b.UserId IS NOT NULL AS is_bot, u.roles = 'system_guest' as is_guest"). "u.DeleteAt as delete_at", "b.UserId IS NOT NULL AS is_bot, u.roles = 'system_guest' as is_guest").
From("Users as u"). From("Users as u").
LeftJoin("Bots b ON ( b.UserId = u.id )"). LeftJoin("Bots b ON ( b.UserId = u.id )").
@ -358,7 +400,7 @@ func (s *MattermostAuthLayer) GetUsersList(userIDs []string) ([]*model.User, err
func (s *MattermostAuthLayer) SearchUsersByTeam(teamID string, searchQuery string, asGuestID string, excludeBots bool) ([]*model.User, error) { func (s *MattermostAuthLayer) SearchUsersByTeam(teamID string, searchQuery string, asGuestID string, excludeBots bool) ([]*model.User, error) {
query := s.getQueryBuilder(). query := s.getQueryBuilder().
Select("u.id", "u.username", "u.email", "u.nickname", "u.firstname", "u.lastname", "u.props", "u.CreateAt as create_at", "u.UpdateAt as update_at", Select("u.id", "u.username", "u.email", "u.nickname", "u.firstname", "u.lastname", "u.CreateAt as create_at", "u.UpdateAt as update_at",
"u.DeleteAt as delete_at", "b.UserId IS NOT NULL AS is_bot, u.roles = 'system_guest' as is_guest"). "u.DeleteAt as delete_at", "b.UserId IS NOT NULL AS is_bot, u.roles = 'system_guest' as is_guest").
From("Users as u"). From("Users as u").
LeftJoin("Bots b ON ( b.UserId = u.id )"). LeftJoin("Bots b ON ( b.UserId = u.id )").
@ -414,7 +456,6 @@ func (s *MattermostAuthLayer) usersFromRows(rows *sql.Rows) ([]*model.User, erro
for rows.Next() { for rows.Next() {
var user model.User var user model.User
var propsBytes []byte
err := rows.Scan( err := rows.Scan(
&user.ID, &user.ID,
@ -423,7 +464,6 @@ func (s *MattermostAuthLayer) usersFromRows(rows *sql.Rows) ([]*model.User, erro
&user.Nickname, &user.Nickname,
&user.FirstName, &user.FirstName,
&user.LastName, &user.LastName,
&propsBytes,
&user.CreateAt, &user.CreateAt,
&user.UpdateAt, &user.UpdateAt,
&user.DeleteAt, &user.DeleteAt,
@ -434,11 +474,6 @@ func (s *MattermostAuthLayer) usersFromRows(rows *sql.Rows) ([]*model.User, erro
return nil, err return nil, err
} }
err = json.Unmarshal(propsBytes, &user.Props)
if err != nil {
return nil, err
}
users = append(users, &user) users = append(users, &user)
} }
@ -464,10 +499,6 @@ func (s *MattermostAuthLayer) CreatePrivateWorkspace(userID string) (string, err
} }
func mmUserToFbUser(mmUser *mmModel.User) model.User { func mmUserToFbUser(mmUser *mmModel.User) model.User {
props := map[string]interface{}{}
for key, value := range mmUser.Props {
props[key] = value
}
authData := "" authData := ""
if mmUser.AuthData != nil { if mmUser.AuthData != nil {
authData = *mmUser.AuthData authData = *mmUser.AuthData
@ -483,7 +514,6 @@ func mmUserToFbUser(mmUser *mmModel.User) model.User {
MfaSecret: mmUser.MfaSecret, MfaSecret: mmUser.MfaSecret,
AuthService: mmUser.AuthService, AuthService: mmUser.AuthService,
AuthData: authData, AuthData: authData,
Props: props,
CreateAt: mmUser.CreateAt, CreateAt: mmUser.CreateAt,
UpdateAt: mmUser.UpdateAt, UpdateAt: mmUser.UpdateAt,
DeleteAt: mmUser.DeleteAt, DeleteAt: mmUser.DeleteAt,

View File

@ -154,11 +154,12 @@ func (mr *MockStoreMockRecorder) CreateSubscription(arg0 interface{}) *gomock.Ca
} }
// CreateUser mocks base method. // CreateUser mocks base method.
func (m *MockStore) CreateUser(arg0 *model.User) error { func (m *MockStore) CreateUser(arg0 *model.User) (*model.User, error) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "CreateUser", arg0) ret := m.ctrl.Call(m, "CreateUser", arg0)
ret0, _ := ret[0].(error) ret0, _ := ret[0].(*model.User)
return ret0 ret1, _ := ret[1].(error)
return ret0, ret1
} }
// CreateUser indicates an expected call of CreateUser. // CreateUser indicates an expected call of CreateUser.
@ -1282,18 +1283,19 @@ func (mr *MockStoreMockRecorder) PatchBoardsAndBlocks(arg0, arg1 interface{}) *g
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PatchBoardsAndBlocks", reflect.TypeOf((*MockStore)(nil).PatchBoardsAndBlocks), arg0, arg1) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PatchBoardsAndBlocks", reflect.TypeOf((*MockStore)(nil).PatchBoardsAndBlocks), arg0, arg1)
} }
// PatchUserProps mocks base method. // PatchUserPreferences mocks base method.
func (m *MockStore) PatchUserProps(arg0 string, arg1 model.UserPropPatch) error { func (m *MockStore) PatchUserPreferences(arg0 string, arg1 model.UserPreferencesPatch) (model0.Preferences, error) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "PatchUserProps", arg0, arg1) ret := m.ctrl.Call(m, "PatchUserPreferences", arg0, arg1)
ret0, _ := ret[0].(error) ret0, _ := ret[0].(model0.Preferences)
return ret0 ret1, _ := ret[1].(error)
return ret0, ret1
} }
// PatchUserProps indicates an expected call of PatchUserProps. // PatchUserPreferences indicates an expected call of PatchUserPreferences.
func (mr *MockStoreMockRecorder) PatchUserProps(arg0, arg1 interface{}) *gomock.Call { func (mr *MockStoreMockRecorder) PatchUserPreferences(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PatchUserProps", reflect.TypeOf((*MockStore)(nil).PatchUserProps), arg0, arg1) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PatchUserPreferences", reflect.TypeOf((*MockStore)(nil).PatchUserPreferences), arg0, arg1)
} }
// PostMessage mocks base method. // PostMessage mocks base method.
@ -1570,11 +1572,12 @@ func (mr *MockStoreMockRecorder) UpdateSubscribersNotifiedAt(arg0, arg1 interfac
} }
// UpdateUser mocks base method. // UpdateUser mocks base method.
func (m *MockStore) UpdateUser(arg0 *model.User) error { func (m *MockStore) UpdateUser(arg0 *model.User) (*model.User, error) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "UpdateUser", arg0) ret := m.ctrl.Call(m, "UpdateUser", arg0)
ret0, _ := ret[0].(error) ret0, _ := ret[0].(*model.User)
return ret0 ret1, _ := ret[1].(error)
return ret0, ret1
} }
// UpdateUser indicates an expected call of UpdateUser. // UpdateUser indicates an expected call of UpdateUser.

View File

@ -119,7 +119,7 @@ func (s *SQLStore) CreateSubscription(sub *model.Subscription) (*model.Subscript
} }
func (s *SQLStore) CreateUser(user *model.User) error { func (s *SQLStore) CreateUser(user *model.User) (*model.User, error) {
return s.createUser(s.db, user) return s.createUser(s.db, user)
} }
@ -722,8 +722,8 @@ func (s *SQLStore) PatchBoardsAndBlocks(pbab *model.PatchBoardsAndBlocks, userID
} }
func (s *SQLStore) PatchUserProps(userID string, patch model.UserPropPatch) error { func (s *SQLStore) PatchUserPreferences(userID string, patch model.UserPreferencesPatch) (mmModel.Preferences, error) {
return s.patchUserProps(s.db, userID, patch) return s.patchUserPreferences(s.db, userID, patch)
} }
@ -874,7 +874,7 @@ func (s *SQLStore) UpdateSubscribersNotifiedAt(blockID string, notifiedAt int64)
} }
func (s *SQLStore) UpdateUser(user *model.User) error { func (s *SQLStore) UpdateUser(user *model.User) (*model.User, error) {
return s.updateUser(s.db, user) return s.updateUser(s.db, user)
} }

View File

@ -2,7 +2,6 @@ package sqlstore
import ( import (
"database/sql" "database/sql"
"encoding/json"
"errors" "errors"
"fmt" "fmt"
@ -68,7 +67,6 @@ func (s *SQLStore) getUsersByCondition(db sq.BaseRunner, condition interface{},
"mfa_secret", "mfa_secret",
"auth_service", "auth_service",
"auth_data", "auth_data",
"props",
"create_at", "create_at",
"update_at", "update_at",
"delete_at", "delete_at",
@ -125,52 +123,45 @@ func (s *SQLStore) getUserByUsername(db sq.BaseRunner, username string) (*model.
return s.getUserByCondition(db, sq.Eq{"username": username}) return s.getUserByCondition(db, sq.Eq{"username": username})
} }
func (s *SQLStore) createUser(db sq.BaseRunner, user *model.User) error { func (s *SQLStore) createUser(db sq.BaseRunner, user *model.User) (*model.User, error) {
now := utils.GetMillis() now := utils.GetMillis()
user.CreateAt = now
propsBytes, err := json.Marshal(user.Props) user.UpdateAt = now
if err != nil { user.DeleteAt = 0
return err
}
query := s.getQueryBuilder(db).Insert(s.tablePrefix+"users"). query := s.getQueryBuilder(db).Insert(s.tablePrefix+"users").
Columns("id", "username", "email", "password", "mfa_secret", "auth_service", "auth_data", "props", "create_at", "update_at", "delete_at"). Columns("id", "username", "email", "password", "mfa_secret", "auth_service", "auth_data", "create_at", "update_at", "delete_at").
Values(user.ID, user.Username, user.Email, user.Password, user.MfaSecret, user.AuthService, user.AuthData, propsBytes, now, now, 0) Values(user.ID, user.Username, user.Email, user.Password, user.MfaSecret, user.AuthService, user.AuthData, user.CreateAt, user.UpdateAt, user.DeleteAt)
_, err = query.Exec() _, err := query.Exec()
return err return user, err
} }
func (s *SQLStore) updateUser(db sq.BaseRunner, user *model.User) error { func (s *SQLStore) updateUser(db sq.BaseRunner, user *model.User) (*model.User, error) {
now := utils.GetMillis() now := utils.GetMillis()
user.UpdateAt = now
propsBytes, err := json.Marshal(user.Props)
if err != nil {
return err
}
query := s.getQueryBuilder(db).Update(s.tablePrefix+"users"). query := s.getQueryBuilder(db).Update(s.tablePrefix+"users").
Set("username", user.Username). Set("username", user.Username).
Set("email", user.Email). Set("email", user.Email).
Set("props", propsBytes). Set("update_at", user.UpdateAt).
Set("update_at", now).
Where(sq.Eq{"id": user.ID}) Where(sq.Eq{"id": user.ID})
result, err := query.Exec() result, err := query.Exec()
if err != nil { if err != nil {
return err return nil, err
} }
rowCount, err := result.RowsAffected() rowCount, err := result.RowsAffected()
if err != nil { if err != nil {
return err return nil, err
} }
if rowCount < 1 { if rowCount < 1 {
return UserNotFoundError{user.ID} return nil, UserNotFoundError{user.ID}
} }
return nil return user, nil
} }
func (s *SQLStore) updateUserPassword(db sq.BaseRunner, username, password string) error { func (s *SQLStore) updateUserPassword(db sq.BaseRunner, username, password string) error {
@ -246,7 +237,6 @@ func (s *SQLStore) usersFromRows(rows *sql.Rows) ([]*model.User, error) {
for rows.Next() { for rows.Next() {
var user model.User var user model.User
var propsBytes []byte
err := rows.Scan( err := rows.Scan(
&user.ID, &user.ID,
@ -256,7 +246,6 @@ func (s *SQLStore) usersFromRows(rows *sql.Rows) ([]*model.User, error) {
&user.MfaSecret, &user.MfaSecret,
&user.AuthService, &user.AuthService,
&user.AuthData, &user.AuthData,
&propsBytes,
&user.CreateAt, &user.CreateAt,
&user.UpdateAt, &user.UpdateAt,
&user.DeleteAt, &user.DeleteAt,
@ -265,18 +254,18 @@ func (s *SQLStore) usersFromRows(rows *sql.Rows) ([]*model.User, error) {
return nil, err return nil, err
} }
err = json.Unmarshal(propsBytes, &user.Props)
if err != nil {
return nil, err
}
users = append(users, &user) users = append(users, &user)
} }
return users, nil return users, nil
} }
func (s *SQLStore) patchUserProps(db sq.BaseRunner, userID string, patch model.UserPropPatch) error { func (s *SQLStore) patchUserPreferences(db sq.BaseRunner, userID string, patch model.UserPreferencesPatch) (mmModel.Preferences, error) {
preferences, err := s.getUserPreferences(db, userID)
if err != nil {
return nil, err
}
if len(patch.UpdatedFields) > 0 { if len(patch.UpdatedFields) > 0 {
for key, value := range patch.UpdatedFields { for key, value := range patch.UpdatedFields {
preference := mmModel.Preference{ preference := mmModel.Preference{
@ -286,9 +275,18 @@ func (s *SQLStore) patchUserProps(db sq.BaseRunner, userID string, patch model.U
Value: value, Value: value,
} }
if err := s.updateUserProps(db, preference); err != nil { if err := s.updateUserPreference(db, preference); err != nil {
return err return nil, err
} }
newPreferences := mmModel.Preferences{}
for _, existingPreference := range preferences {
if preference.Name != existingPreference.Name {
newPreferences = append(newPreferences, existingPreference)
}
}
newPreferences = append(newPreferences, preference)
preferences = newPreferences
} }
} }
@ -300,16 +298,24 @@ func (s *SQLStore) patchUserProps(db sq.BaseRunner, userID string, patch model.U
Name: key, Name: key,
} }
if err := s.deleteUserProps(db, preference); err != nil { if err := s.deleteUserPreference(db, preference); err != nil {
return err return nil, err
} }
newPreferences := mmModel.Preferences{}
for _, existingPreference := range preferences {
if preference.Name != existingPreference.Name {
newPreferences = append(newPreferences, existingPreference)
}
}
preferences = newPreferences
} }
} }
return nil return preferences, nil
} }
func (s *SQLStore) updateUserProps(db sq.BaseRunner, preference mmModel.Preference) error { func (s *SQLStore) updateUserPreference(db sq.BaseRunner, preference mmModel.Preference) error {
query := s.getQueryBuilder(db). query := s.getQueryBuilder(db).
Insert(s.tablePrefix+"preferences"). Insert(s.tablePrefix+"preferences").
Columns("UserId", "Category", "Name", "Value"). Columns("UserId", "Category", "Name", "Value").
@ -333,7 +339,7 @@ func (s *SQLStore) updateUserProps(db sq.BaseRunner, preference mmModel.Preferen
return nil return nil
} }
func (s *SQLStore) deleteUserProps(db sq.BaseRunner, preference mmModel.Preference) error { func (s *SQLStore) deleteUserPreference(db sq.BaseRunner, preference mmModel.Preference) error {
query := s.getQueryBuilder(db). query := s.getQueryBuilder(db).
Delete(s.tablePrefix + "preferences"). Delete(s.tablePrefix + "preferences").
Where(sq.Eq{"UserId": preference.UserId}). Where(sq.Eq{"UserId": preference.UserId}).

View File

@ -59,13 +59,13 @@ type Store interface {
GetUsersList(userIDs []string) ([]*model.User, error) GetUsersList(userIDs []string) ([]*model.User, error)
GetUserByEmail(email string) (*model.User, error) GetUserByEmail(email string) (*model.User, error)
GetUserByUsername(username string) (*model.User, error) GetUserByUsername(username string) (*model.User, error)
CreateUser(user *model.User) error CreateUser(user *model.User) (*model.User, error)
UpdateUser(user *model.User) error UpdateUser(user *model.User) (*model.User, error)
UpdateUserPassword(username, password string) error UpdateUserPassword(username, password string) error
UpdateUserPasswordByID(userID, password string) error UpdateUserPasswordByID(userID, password string) error
GetUsersByTeam(teamID string, asGuestID string) ([]*model.User, error) GetUsersByTeam(teamID string, asGuestID string) ([]*model.User, error)
SearchUsersByTeam(teamID string, searchQuery string, asGuestID string, excludeBots bool) ([]*model.User, error) SearchUsersByTeam(teamID string, searchQuery string, asGuestID string, excludeBots bool) ([]*model.User, error)
PatchUserProps(userID string, patch model.UserPropPatch) error PatchUserPreferences(userID string, patch model.UserPreferencesPatch) (mmModel.Preferences, error)
GetUserPreferences(userID string) (mmModel.Preferences, error) GetUserPreferences(userID string) (mmModel.Preferences, error)
GetActiveUserCount(updatedSecondsAgo int64) (int, error) GetActiveUserCount(updatedSecondsAgo int64) (int, error)

View File

@ -62,14 +62,17 @@ func testGetUsersByTeam(t *testing.T, store store.Store) {
userID := utils.NewID(utils.IDTypeUser) userID := utils.NewID(utils.IDTypeUser)
err = store.CreateUser(&model.User{ user, err := store.CreateUser(&model.User{
ID: userID, ID: userID,
Username: "darth.vader", Username: "darth.vader",
}) })
require.NoError(t, err) require.NoError(t, err)
require.NotNil(t, user)
require.Equal(t, userID, user.ID)
require.Equal(t, "darth.vader", user.Username)
defer func() { defer func() {
_ = store.UpdateUser(&model.User{ _, _ = store.UpdateUser(&model.User{
ID: userID, ID: userID,
DeleteAt: utils.GetMillis(), DeleteAt: utils.GetMillis(),
}) })
@ -90,8 +93,9 @@ func testCreateAndGetUser(t *testing.T, store store.Store) {
} }
t.Run("CreateUser", func(t *testing.T) { t.Run("CreateUser", func(t *testing.T) {
err := store.CreateUser(user) newUser, err := store.CreateUser(user)
require.NoError(t, err) require.NoError(t, err)
require.NotNil(t, newUser)
}) })
t.Run("GetUserByID", func(t *testing.T) { t.Run("GetUserByID", func(t *testing.T) {
@ -147,8 +151,9 @@ func testGetUsersList(t *testing.T, store store.Store) {
Username: fmt.Sprintf("%s-username", id), Username: fmt.Sprintf("%s-username", id),
Email: fmt.Sprintf("%s@sample.com", id), Email: fmt.Sprintf("%s@sample.com", id),
} }
err := store.CreateUser(user) newUser, err := store.CreateUser(user)
require.NoError(t, err) require.NoError(t, err)
require.NotNil(t, newUser)
} }
testCases := []struct { testCases := []struct {
@ -200,22 +205,24 @@ func testCreateAndUpdateUser(t *testing.T, store store.Store) {
user := &model.User{ user := &model.User{
ID: utils.NewID(utils.IDTypeUser), ID: utils.NewID(utils.IDTypeUser),
} }
err := store.CreateUser(user) newUser, err := store.CreateUser(user)
require.NoError(t, err) require.NoError(t, err)
require.NotNil(t, newUser)
t.Run("UpdateUser", func(t *testing.T) { t.Run("UpdateUser", func(t *testing.T) {
user.Username = "damao" user.Username = "damao"
user.Email = "mock@email.com" user.Email = "mock@email.com"
user.Props = map[string]interface{}{"a": "b"} uUser, err := store.UpdateUser(user)
err := store.UpdateUser(user)
require.NoError(t, err) require.NoError(t, err)
require.NotNil(t, uUser)
require.Equal(t, user.Username, uUser.Username)
require.Equal(t, user.Email, uUser.Email)
got, err := store.GetUserByID(user.ID) got, err := store.GetUserByID(user.ID)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, user.ID, got.ID) require.Equal(t, user.ID, got.ID)
require.Equal(t, user.Username, got.Username) require.Equal(t, user.Username, got.Username)
require.Equal(t, user.Email, got.Email) require.Equal(t, user.Email, got.Email)
require.Equal(t, user.Props, got.Props)
}) })
t.Run("UpdateUserPassword", func(t *testing.T) { t.Run("UpdateUserPassword", func(t *testing.T) {
@ -244,10 +251,11 @@ func testCreateAndUpdateUser(t *testing.T, store store.Store) {
func testCreateAndGetRegisteredUserCount(t *testing.T, store store.Store) { func testCreateAndGetRegisteredUserCount(t *testing.T, store store.Store) {
randomN := int(time.Now().Unix() % 10) randomN := int(time.Now().Unix() % 10)
for i := 0; i < randomN; i++ { for i := 0; i < randomN; i++ {
err := store.CreateUser(&model.User{ user, err := store.CreateUser(&model.User{
ID: utils.NewID(utils.IDTypeUser), ID: utils.NewID(utils.IDTypeUser),
}) })
require.NoError(t, err) require.NoError(t, err)
require.NotNil(t, user)
} }
got, err := store.GetRegisteredUserCount() got, err := store.GetRegisteredUserCount()
@ -259,15 +267,16 @@ func testPatchUserProps(t *testing.T, store store.Store) {
user := &model.User{ user := &model.User{
ID: utils.NewID(utils.IDTypeUser), ID: utils.NewID(utils.IDTypeUser),
} }
err := store.CreateUser(user) newUser, err := store.CreateUser(user)
require.NoError(t, err) require.NoError(t, err)
require.NotNil(t, newUser)
key1 := "new_key_1" key1 := "new_key_1"
key2 := "new_key_2" key2 := "new_key_2"
key3 := "new_key_3" key3 := "new_key_3"
// Only update props // Only update props
patch := model.UserPropPatch{ patch := model.UserPreferencesPatch{
UpdatedFields: map[string]string{ UpdatedFields: map[string]string{
key1: "new_value_1", key1: "new_value_1",
key2: "new_value_2", key2: "new_value_2",
@ -275,9 +284,7 @@ func testPatchUserProps(t *testing.T, store store.Store) {
}, },
} }
err = store.PatchUserProps(user.ID, patch) userPreferences, err := store.PatchUserPreferences(user.ID, patch)
require.NoError(t, err)
userPreferences, err := store.GetUserPreferences(user.ID)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 3, len(userPreferences)) require.Equal(t, 3, len(userPreferences))
@ -293,15 +300,13 @@ func testPatchUserProps(t *testing.T, store store.Store) {
} }
// Delete a prop // Delete a prop
patch = model.UserPropPatch{ patch = model.UserPreferencesPatch{
DeletedFields: []string{ DeletedFields: []string{
key1, key1,
}, },
} }
err = store.PatchUserProps(user.ID, patch) userPreferences, err = store.PatchUserPreferences(user.ID, patch)
require.NoError(t, err)
userPreferences, err = store.GetUserPreferences(user.ID)
require.NoError(t, err) require.NoError(t, err)
for _, preference := range userPreferences { for _, preference := range userPreferences {
@ -316,7 +321,7 @@ func testPatchUserProps(t *testing.T, store store.Store) {
} }
// update and delete together // update and delete together
patch = model.UserPropPatch{ patch = model.UserPreferencesPatch{
UpdatedFields: map[string]string{ UpdatedFields: map[string]string{
key3: "new_value_3_new_again", key3: "new_value_3_new_again",
}, },
@ -324,9 +329,7 @@ func testPatchUserProps(t *testing.T, store store.Store) {
key2, key2,
}, },
} }
err = store.PatchUserProps(user.ID, patch) userPreferences, err = store.PatchUserPreferences(user.ID, patch)
require.NoError(t, err)
userPreferences, err = store.GetUserPreferences(user.ID)
require.NoError(t, err) require.NoError(t, err)
for _, preference := range userPreferences { for _, preference := range userPreferences {

View File

@ -21,8 +21,9 @@ func createTestUsers(t *testing.T, store store.Store, num int) []*model.User {
Username: fmt.Sprintf("mooncake.%d", i), Username: fmt.Sprintf("mooncake.%d", i),
Email: fmt.Sprintf("mooncake.%d@example.com", i), Email: fmt.Sprintf("mooncake.%d@example.com", i),
} }
err := store.CreateUser(user) newUser, err := store.CreateUser(user)
require.NoError(t, err) require.NoError(t, err)
require.NotNil(t, newUser)
users = append(users, user) users = append(users, user)
} }