You've already forked focalboard
							
							
				mirror of
				https://github.com/mattermost/focalboard.git
				synced 2025-10-31 00:17:42 +02:00 
			
		
		
		
	Update user store to return the updated entities after inserting / patching them (#3843)
This commit is contained in:
		
				
					committed by
					
						 GitHub
						GitHub
					
				
			
			
				
	
			
			
			
						parent
						
							f574286e6e
						
					
				
				
					commit
					dfa9d8f452
				
			| @@ -115,7 +115,6 @@ func (a *API) handleGetMe(w http.ResponseWriter, r *http.Request) { | ||||
| 			Email:    model.SingleUser, | ||||
| 			CreateAt: ws.UpdateAt, | ||||
| 			UpdateAt: now, | ||||
| 			Props:    map[string]interface{}{}, | ||||
| 		} | ||||
| 	} else { | ||||
| 		user, err = a.app.GetUser(userID) | ||||
| @@ -280,7 +279,7 @@ func (a *API) handleUpdateUserConfig(w http.ResponseWriter, r *http.Request) { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	var patch *model.UserPropPatch | ||||
| 	var patch *model.UserPreferencesPatch | ||||
| 	err = json.Unmarshal(requestBody, &patch) | ||||
| 	if err != nil { | ||||
| 		a.errorResponse(w, r, err) | ||||
|   | ||||
| @@ -178,7 +178,7 @@ func (a *App) RegisterUser(username, email, password string) error { | ||||
| 		return errors.Wrap(err, "Invalid password") | ||||
| 	} | ||||
|  | ||||
| 	err = a.store.CreateUser(&model.User{ | ||||
| 	_, err = a.store.CreateUser(&model.User{ | ||||
| 		ID:          utils.NewID(utils.IDTypeUser), | ||||
| 		Username:    username, | ||||
| 		Email:       email, | ||||
| @@ -186,7 +186,6 @@ func (a *App) RegisterUser(username, email, password string) error { | ||||
| 		MfaSecret:   "", | ||||
| 		AuthService: a.config.AuthMode, | ||||
| 		AuthData:    "", | ||||
| 		Props:       map[string]interface{}{}, | ||||
| 	}) | ||||
| 	if err != nil { | ||||
| 		return errors.Wrap(err, "Unable to create the new user") | ||||
|   | ||||
| @@ -109,7 +109,7 @@ func TestRegisterUser(t *testing.T) { | ||||
| 	th.Store.EXPECT().GetUserByUsername("newUsername").Return(mockUser, errors.New("user not found")) | ||||
| 	th.Store.EXPECT().GetUserByEmail("existingEmail").Return(mockUser, nil) | ||||
| 	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 { | ||||
| 		t.Run(test.title, func(t *testing.T) { | ||||
|   | ||||
| @@ -30,14 +30,14 @@ func (a *App) PrepareOnboardingTour(userID string, teamID string) (string, strin | ||||
| 	} | ||||
|  | ||||
| 	// set user's tour state to initial state | ||||
| 	userPropPatch := model.UserPropPatch{ | ||||
| 	userPreferencesPatch := model.UserPreferencesPatch{ | ||||
| 		UpdatedFields: map[string]string{ | ||||
| 			KeyOnboardingTourStarted:  "1", | ||||
| 			KeyOnboardingTourStep:     ValueOnboardingFirstStep, | ||||
| 			KeyOnboardingTourCategory: ValueTourCategoryOnboarding, | ||||
| 		}, | ||||
| 	} | ||||
| 	if err := a.store.PatchUserProps(userID, userPropPatch); err != nil { | ||||
| 	if _, err := a.store.PatchUserPreferences(userID, userPreferencesPatch); err != nil { | ||||
| 		return "", "", err | ||||
| 	} | ||||
|  | ||||
|   | ||||
| @@ -42,7 +42,7 @@ func TestPrepareOnboardingTour(t *testing.T) { | ||||
| 		newType := model.BoardTypePrivate | ||||
| 		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{ | ||||
| 				KeyOnboardingTourStarted:  "1", | ||||
| 				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) | ||||
|  | ||||
| 		teamID, boardID, err := th.App.PrepareOnboardingTour(userID, teamID) | ||||
|   | ||||
| @@ -13,12 +13,8 @@ func (a *App) SearchTeamUsers(teamID string, searchQuery string, asGuestID strin | ||||
| 	return a.store.SearchUsersByTeam(teamID, searchQuery, asGuestID, excludeBots) | ||||
| } | ||||
|  | ||||
| func (a *App) UpdateUserConfig(userID string, patch model.UserPropPatch) ([]mmModel.Preference, error) { | ||||
| 	if err := a.store.PatchUserProps(userID, patch); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	updatedPreferences, err := a.store.GetUserPreferences(userID) | ||||
| func (a *App) UpdateUserConfig(userID string, patch model.UserPreferencesPatch) ([]mmModel.Preference, error) { | ||||
| 	updatedPreferences, err := a.store.PatchUserPreferences(userID, patch) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|   | ||||
| @@ -2561,7 +2561,7 @@ func TestPermissionsUserChangePassword(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{ | ||||
| 		{"/users/{USER_TEAM_MEMBER_ID}/config", methodPut, patch, userAnon, http.StatusUnauthorized, 0}, | ||||
|   | ||||
| @@ -27,7 +27,6 @@ func NewPluginTestStore(innerStore store.Store) *PluginTestStore { | ||||
| 		users: map[string]*model.User{ | ||||
| 			"no-team-member": { | ||||
| 				ID:       "no-team-member", | ||||
| 				Props:    map[string]interface{}{}, | ||||
| 				Username: "no-team-member", | ||||
| 				Email:    "no-team-member@sample.com", | ||||
| 				CreateAt: model.GetMillis(), | ||||
| @@ -35,7 +34,6 @@ func NewPluginTestStore(innerStore store.Store) *PluginTestStore { | ||||
| 			}, | ||||
| 			"team-member": { | ||||
| 				ID:       "team-member", | ||||
| 				Props:    map[string]interface{}{}, | ||||
| 				Username: "team-member", | ||||
| 				Email:    "team-member@sample.com", | ||||
| 				CreateAt: model.GetMillis(), | ||||
| @@ -43,7 +41,6 @@ func NewPluginTestStore(innerStore store.Store) *PluginTestStore { | ||||
| 			}, | ||||
| 			"viewer": { | ||||
| 				ID:       "viewer", | ||||
| 				Props:    map[string]interface{}{}, | ||||
| 				Username: "viewer", | ||||
| 				Email:    "viewer@sample.com", | ||||
| 				CreateAt: model.GetMillis(), | ||||
| @@ -51,7 +48,6 @@ func NewPluginTestStore(innerStore store.Store) *PluginTestStore { | ||||
| 			}, | ||||
| 			"commenter": { | ||||
| 				ID:       "commenter", | ||||
| 				Props:    map[string]interface{}{}, | ||||
| 				Username: "commenter", | ||||
| 				Email:    "commenter@sample.com", | ||||
| 				CreateAt: model.GetMillis(), | ||||
| @@ -59,7 +55,6 @@ func NewPluginTestStore(innerStore store.Store) *PluginTestStore { | ||||
| 			}, | ||||
| 			"editor": { | ||||
| 				ID:       "editor", | ||||
| 				Props:    map[string]interface{}{}, | ||||
| 				Username: "editor", | ||||
| 				Email:    "editor@sample.com", | ||||
| 				CreateAt: model.GetMillis(), | ||||
| @@ -67,7 +62,6 @@ func NewPluginTestStore(innerStore store.Store) *PluginTestStore { | ||||
| 			}, | ||||
| 			"admin": { | ||||
| 				ID:       "admin", | ||||
| 				Props:    map[string]interface{}{}, | ||||
| 				Username: "admin", | ||||
| 				Email:    "admin@sample.com", | ||||
| 				CreateAt: model.GetMillis(), | ||||
| @@ -75,7 +69,6 @@ func NewPluginTestStore(innerStore store.Store) *PluginTestStore { | ||||
| 			}, | ||||
| 			"guest": { | ||||
| 				ID:       "guest", | ||||
| 				Props:    map[string]interface{}{}, | ||||
| 				Username: "guest", | ||||
| 				Email:    "guest@sample.com", | ||||
| 				CreateAt: model.GetMillis(), | ||||
| @@ -150,27 +143,6 @@ func (s *PluginTestStore) GetUserByUsername(username string) (*model.User, error | ||||
| 	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) { | ||||
| 	if userID == userTeamMember { | ||||
| 		return mmModel.Preferences{{ | ||||
|   | ||||
| @@ -46,10 +46,6 @@ type User struct { | ||||
| 	// swagger:ignore | ||||
| 	AuthData string `json:"-"` | ||||
|  | ||||
| 	// User settings | ||||
| 	// required: true | ||||
| 	Props map[string]interface{} `json:"props"` | ||||
|  | ||||
| 	// Created time in miliseconds since the current epoch | ||||
| 	// required: true | ||||
| 	CreateAt int64 `json:"create_at,omitempty"` | ||||
| @@ -73,14 +69,14 @@ type User struct { | ||||
| 	Roles string `json:"roles"` | ||||
| } | ||||
|  | ||||
| // UserPropPatch is a user property patch | ||||
| // UserPreferencesPatch is a user property patch | ||||
| // swagger:model | ||||
| type UserPropPatch struct { | ||||
| 	// The user prop updated fields | ||||
| type UserPreferencesPatch struct { | ||||
| 	// The user preference updated fields | ||||
| 	// required: false | ||||
| 	UpdatedFields map[string]string `json:"updatedFields"` | ||||
|  | ||||
| 	// The user prop removed fields | ||||
| 	// The user preference removed fields | ||||
| 	// required: false | ||||
| 	DeletedFields []string `json:"deletedFields"` | ||||
| } | ||||
|   | ||||
| @@ -115,12 +115,12 @@ func (s *MattermostAuthLayer) GetUserByUsername(username string) (*model.User, e | ||||
| 	return &user, nil | ||||
| } | ||||
|  | ||||
| func (s *MattermostAuthLayer) CreateUser(user *model.User) error { | ||||
| 	return store.NewNotSupportedError("no user creation allowed from focalboard, create it using mattermost") | ||||
| func (s *MattermostAuthLayer) CreateUser(user *model.User) (*model.User, error) { | ||||
| 	return nil, store.NewNotSupportedError("no user creation allowed from focalboard, create it using mattermost") | ||||
| } | ||||
|  | ||||
| func (s *MattermostAuthLayer) UpdateUser(user *model.User) error { | ||||
| 	return store.NewNotSupportedError("no update allowed from focalboard, update it using mattermost") | ||||
| func (s *MattermostAuthLayer) UpdateUser(user *model.User) (*model.User, error) { | ||||
| 	return nil, store.NewNotSupportedError("no update allowed from focalboard, update it using mattermost") | ||||
| } | ||||
|  | ||||
| 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") | ||||
| } | ||||
|  | ||||
| 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 { | ||||
| 		updatedPreferences := mmModel.Preferences{} | ||||
| 		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 { | ||||
| 			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 { | ||||
| @@ -165,11 +189,29 @@ func (s *MattermostAuthLayer) PatchUserProps(userID string, patch model.UserProp | ||||
|  | ||||
| 		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)) | ||||
| 			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) { | ||||
| @@ -291,7 +333,7 @@ func (s *MattermostAuthLayer) getQueryBuilder() sq.StatementBuilderType { | ||||
|  | ||||
| func (s *MattermostAuthLayer) GetUsersByTeam(teamID string, asGuestID string) ([]*model.User, error) { | ||||
| 	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"). | ||||
| 		From("Users as u"). | ||||
| 		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) { | ||||
| 	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"). | ||||
| 		From("Users as u"). | ||||
| 		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) { | ||||
| 	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"). | ||||
| 		From("Users as u"). | ||||
| 		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() { | ||||
| 		var user model.User | ||||
| 		var propsBytes []byte | ||||
|  | ||||
| 		err := rows.Scan( | ||||
| 			&user.ID, | ||||
| @@ -423,7 +464,6 @@ func (s *MattermostAuthLayer) usersFromRows(rows *sql.Rows) ([]*model.User, erro | ||||
| 			&user.Nickname, | ||||
| 			&user.FirstName, | ||||
| 			&user.LastName, | ||||
| 			&propsBytes, | ||||
| 			&user.CreateAt, | ||||
| 			&user.UpdateAt, | ||||
| 			&user.DeleteAt, | ||||
| @@ -434,11 +474,6 @@ func (s *MattermostAuthLayer) usersFromRows(rows *sql.Rows) ([]*model.User, erro | ||||
| 			return nil, err | ||||
| 		} | ||||
|  | ||||
| 		err = json.Unmarshal(propsBytes, &user.Props) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
|  | ||||
| 		users = append(users, &user) | ||||
| 	} | ||||
|  | ||||
| @@ -464,10 +499,6 @@ func (s *MattermostAuthLayer) CreatePrivateWorkspace(userID string) (string, err | ||||
| } | ||||
|  | ||||
| func mmUserToFbUser(mmUser *mmModel.User) model.User { | ||||
| 	props := map[string]interface{}{} | ||||
| 	for key, value := range mmUser.Props { | ||||
| 		props[key] = value | ||||
| 	} | ||||
| 	authData := "" | ||||
| 	if mmUser.AuthData != nil { | ||||
| 		authData = *mmUser.AuthData | ||||
| @@ -483,7 +514,6 @@ func mmUserToFbUser(mmUser *mmModel.User) model.User { | ||||
| 		MfaSecret:   mmUser.MfaSecret, | ||||
| 		AuthService: mmUser.AuthService, | ||||
| 		AuthData:    authData, | ||||
| 		Props:       props, | ||||
| 		CreateAt:    mmUser.CreateAt, | ||||
| 		UpdateAt:    mmUser.UpdateAt, | ||||
| 		DeleteAt:    mmUser.DeleteAt, | ||||
|   | ||||
| @@ -154,11 +154,12 @@ func (mr *MockStoreMockRecorder) CreateSubscription(arg0 interface{}) *gomock.Ca | ||||
| } | ||||
|  | ||||
| // 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() | ||||
| 	ret := m.ctrl.Call(m, "CreateUser", arg0) | ||||
| 	ret0, _ := ret[0].(error) | ||||
| 	return ret0 | ||||
| 	ret0, _ := ret[0].(*model.User) | ||||
| 	ret1, _ := ret[1].(error) | ||||
| 	return ret0, ret1 | ||||
| } | ||||
|  | ||||
| // 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) | ||||
| } | ||||
|  | ||||
| // PatchUserProps mocks base method. | ||||
| func (m *MockStore) PatchUserProps(arg0 string, arg1 model.UserPropPatch) error { | ||||
| // PatchUserPreferences mocks base method. | ||||
| func (m *MockStore) PatchUserPreferences(arg0 string, arg1 model.UserPreferencesPatch) (model0.Preferences, error) { | ||||
| 	m.ctrl.T.Helper() | ||||
| 	ret := m.ctrl.Call(m, "PatchUserProps", arg0, arg1) | ||||
| 	ret0, _ := ret[0].(error) | ||||
| 	return ret0 | ||||
| 	ret := m.ctrl.Call(m, "PatchUserPreferences", arg0, arg1) | ||||
| 	ret0, _ := ret[0].(model0.Preferences) | ||||
| 	ret1, _ := ret[1].(error) | ||||
| 	return ret0, ret1 | ||||
| } | ||||
|  | ||||
| // PatchUserProps indicates an expected call of PatchUserProps. | ||||
| func (mr *MockStoreMockRecorder) PatchUserProps(arg0, arg1 interface{}) *gomock.Call { | ||||
| // PatchUserPreferences indicates an expected call of PatchUserPreferences. | ||||
| func (mr *MockStoreMockRecorder) PatchUserPreferences(arg0, arg1 interface{}) *gomock.Call { | ||||
| 	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. | ||||
| @@ -1570,11 +1572,12 @@ func (mr *MockStoreMockRecorder) UpdateSubscribersNotifiedAt(arg0, arg1 interfac | ||||
| } | ||||
|  | ||||
| // 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() | ||||
| 	ret := m.ctrl.Call(m, "UpdateUser", arg0) | ||||
| 	ret0, _ := ret[0].(error) | ||||
| 	return ret0 | ||||
| 	ret0, _ := ret[0].(*model.User) | ||||
| 	ret1, _ := ret[1].(error) | ||||
| 	return ret0, ret1 | ||||
| } | ||||
|  | ||||
| // UpdateUser indicates an expected call of UpdateUser. | ||||
|   | ||||
| @@ -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) | ||||
|  | ||||
| } | ||||
| @@ -722,8 +722,8 @@ func (s *SQLStore) PatchBoardsAndBlocks(pbab *model.PatchBoardsAndBlocks, userID | ||||
|  | ||||
| } | ||||
|  | ||||
| func (s *SQLStore) PatchUserProps(userID string, patch model.UserPropPatch) error { | ||||
| 	return s.patchUserProps(s.db, userID, patch) | ||||
| func (s *SQLStore) PatchUserPreferences(userID string, patch model.UserPreferencesPatch) (mmModel.Preferences, error) { | ||||
| 	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) | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -2,7 +2,6 @@ package sqlstore | ||||
|  | ||||
| import ( | ||||
| 	"database/sql" | ||||
| 	"encoding/json" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
|  | ||||
| @@ -68,7 +67,6 @@ func (s *SQLStore) getUsersByCondition(db sq.BaseRunner, condition interface{}, | ||||
| 			"mfa_secret", | ||||
| 			"auth_service", | ||||
| 			"auth_data", | ||||
| 			"props", | ||||
| 			"create_at", | ||||
| 			"update_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}) | ||||
| } | ||||
|  | ||||
| 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() | ||||
|  | ||||
| 	propsBytes, err := json.Marshal(user.Props) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	user.CreateAt = now | ||||
| 	user.UpdateAt = now | ||||
| 	user.DeleteAt = 0 | ||||
|  | ||||
| 	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"). | ||||
| 		Values(user.ID, user.Username, user.Email, user.Password, user.MfaSecret, user.AuthService, user.AuthData, propsBytes, now, now, 0) | ||||
| 		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, user.CreateAt, user.UpdateAt, user.DeleteAt) | ||||
|  | ||||
| 	_, err = query.Exec() | ||||
| 	return err | ||||
| 	_, err := query.Exec() | ||||
| 	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() | ||||
|  | ||||
| 	propsBytes, err := json.Marshal(user.Props) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	user.UpdateAt = now | ||||
|  | ||||
| 	query := s.getQueryBuilder(db).Update(s.tablePrefix+"users"). | ||||
| 		Set("username", user.Username). | ||||
| 		Set("email", user.Email). | ||||
| 		Set("props", propsBytes). | ||||
| 		Set("update_at", now). | ||||
| 		Set("update_at", user.UpdateAt). | ||||
| 		Where(sq.Eq{"id": user.ID}) | ||||
|  | ||||
| 	result, err := query.Exec() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	rowCount, err := result.RowsAffected() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	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 { | ||||
| @@ -246,7 +237,6 @@ func (s *SQLStore) usersFromRows(rows *sql.Rows) ([]*model.User, error) { | ||||
|  | ||||
| 	for rows.Next() { | ||||
| 		var user model.User | ||||
| 		var propsBytes []byte | ||||
|  | ||||
| 		err := rows.Scan( | ||||
| 			&user.ID, | ||||
| @@ -256,7 +246,6 @@ func (s *SQLStore) usersFromRows(rows *sql.Rows) ([]*model.User, error) { | ||||
| 			&user.MfaSecret, | ||||
| 			&user.AuthService, | ||||
| 			&user.AuthData, | ||||
| 			&propsBytes, | ||||
| 			&user.CreateAt, | ||||
| 			&user.UpdateAt, | ||||
| 			&user.DeleteAt, | ||||
| @@ -265,18 +254,18 @@ func (s *SQLStore) usersFromRows(rows *sql.Rows) ([]*model.User, error) { | ||||
| 			return nil, err | ||||
| 		} | ||||
|  | ||||
| 		err = json.Unmarshal(propsBytes, &user.Props) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
|  | ||||
| 		users = append(users, &user) | ||||
| 	} | ||||
|  | ||||
| 	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 { | ||||
| 		for key, value := range patch.UpdatedFields { | ||||
| 			preference := mmModel.Preference{ | ||||
| @@ -286,9 +275,18 @@ func (s *SQLStore) patchUserProps(db sq.BaseRunner, userID string, patch model.U | ||||
| 				Value:    value, | ||||
| 			} | ||||
|  | ||||
| 			if err := s.updateUserProps(db, preference); err != nil { | ||||
| 				return err | ||||
| 			if err := s.updateUserPreference(db, preference); err != nil { | ||||
| 				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, | ||||
| 			} | ||||
|  | ||||
| 			if err := s.deleteUserProps(db, preference); err != nil { | ||||
| 				return err | ||||
| 			if err := s.deleteUserPreference(db, preference); err != nil { | ||||
| 				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). | ||||
| 		Insert(s.tablePrefix+"preferences"). | ||||
| 		Columns("UserId", "Category", "Name", "Value"). | ||||
| @@ -333,7 +339,7 @@ func (s *SQLStore) updateUserProps(db sq.BaseRunner, preference mmModel.Preferen | ||||
| 	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). | ||||
| 		Delete(s.tablePrefix + "preferences"). | ||||
| 		Where(sq.Eq{"UserId": preference.UserId}). | ||||
|   | ||||
| @@ -59,13 +59,13 @@ type Store interface { | ||||
| 	GetUsersList(userIDs []string) ([]*model.User, error) | ||||
| 	GetUserByEmail(email string) (*model.User, error) | ||||
| 	GetUserByUsername(username string) (*model.User, error) | ||||
| 	CreateUser(user *model.User) error | ||||
| 	UpdateUser(user *model.User) error | ||||
| 	CreateUser(user *model.User) (*model.User, error) | ||||
| 	UpdateUser(user *model.User) (*model.User, error) | ||||
| 	UpdateUserPassword(username, password string) error | ||||
| 	UpdateUserPasswordByID(userID, password string) error | ||||
| 	GetUsersByTeam(teamID string, asGuestID string) ([]*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) | ||||
|  | ||||
| 	GetActiveUserCount(updatedSecondsAgo int64) (int, error) | ||||
|   | ||||
| @@ -62,14 +62,17 @@ func testGetUsersByTeam(t *testing.T, store store.Store) { | ||||
|  | ||||
| 		userID := utils.NewID(utils.IDTypeUser) | ||||
|  | ||||
| 		err = store.CreateUser(&model.User{ | ||||
| 		user, err := store.CreateUser(&model.User{ | ||||
| 			ID:       userID, | ||||
| 			Username: "darth.vader", | ||||
| 		}) | ||||
| 		require.NoError(t, err) | ||||
| 		require.NotNil(t, user) | ||||
| 		require.Equal(t, userID, user.ID) | ||||
| 		require.Equal(t, "darth.vader", user.Username) | ||||
|  | ||||
| 		defer func() { | ||||
| 			_ = store.UpdateUser(&model.User{ | ||||
| 			_, _ = store.UpdateUser(&model.User{ | ||||
| 				ID:       userID, | ||||
| 				DeleteAt: utils.GetMillis(), | ||||
| 			}) | ||||
| @@ -90,8 +93,9 @@ func testCreateAndGetUser(t *testing.T, store store.Store) { | ||||
| 	} | ||||
|  | ||||
| 	t.Run("CreateUser", func(t *testing.T) { | ||||
| 		err := store.CreateUser(user) | ||||
| 		newUser, err := store.CreateUser(user) | ||||
| 		require.NoError(t, err) | ||||
| 		require.NotNil(t, newUser) | ||||
| 	}) | ||||
|  | ||||
| 	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), | ||||
| 			Email:    fmt.Sprintf("%s@sample.com", id), | ||||
| 		} | ||||
| 		err := store.CreateUser(user) | ||||
| 		newUser, err := store.CreateUser(user) | ||||
| 		require.NoError(t, err) | ||||
| 		require.NotNil(t, newUser) | ||||
| 	} | ||||
|  | ||||
| 	testCases := []struct { | ||||
| @@ -200,22 +205,24 @@ func testCreateAndUpdateUser(t *testing.T, store store.Store) { | ||||
| 	user := &model.User{ | ||||
| 		ID: utils.NewID(utils.IDTypeUser), | ||||
| 	} | ||||
| 	err := store.CreateUser(user) | ||||
| 	newUser, err := store.CreateUser(user) | ||||
| 	require.NoError(t, err) | ||||
| 	require.NotNil(t, newUser) | ||||
|  | ||||
| 	t.Run("UpdateUser", func(t *testing.T) { | ||||
| 		user.Username = "damao" | ||||
| 		user.Email = "mock@email.com" | ||||
| 		user.Props = map[string]interface{}{"a": "b"} | ||||
| 		err := store.UpdateUser(user) | ||||
| 		uUser, err := store.UpdateUser(user) | ||||
| 		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) | ||||
| 		require.NoError(t, err) | ||||
| 		require.Equal(t, user.ID, got.ID) | ||||
| 		require.Equal(t, user.Username, got.Username) | ||||
| 		require.Equal(t, user.Email, got.Email) | ||||
| 		require.Equal(t, user.Props, got.Props) | ||||
| 	}) | ||||
|  | ||||
| 	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) { | ||||
| 	randomN := int(time.Now().Unix() % 10) | ||||
| 	for i := 0; i < randomN; i++ { | ||||
| 		err := store.CreateUser(&model.User{ | ||||
| 		user, err := store.CreateUser(&model.User{ | ||||
| 			ID: utils.NewID(utils.IDTypeUser), | ||||
| 		}) | ||||
| 		require.NoError(t, err) | ||||
| 		require.NotNil(t, user) | ||||
| 	} | ||||
|  | ||||
| 	got, err := store.GetRegisteredUserCount() | ||||
| @@ -259,15 +267,16 @@ func testPatchUserProps(t *testing.T, store store.Store) { | ||||
| 	user := &model.User{ | ||||
| 		ID: utils.NewID(utils.IDTypeUser), | ||||
| 	} | ||||
| 	err := store.CreateUser(user) | ||||
| 	newUser, err := store.CreateUser(user) | ||||
| 	require.NoError(t, err) | ||||
| 	require.NotNil(t, newUser) | ||||
|  | ||||
| 	key1 := "new_key_1" | ||||
| 	key2 := "new_key_2" | ||||
| 	key3 := "new_key_3" | ||||
|  | ||||
| 	// Only update props | ||||
| 	patch := model.UserPropPatch{ | ||||
| 	patch := model.UserPreferencesPatch{ | ||||
| 		UpdatedFields: map[string]string{ | ||||
| 			key1: "new_value_1", | ||||
| 			key2: "new_value_2", | ||||
| @@ -275,9 +284,7 @@ func testPatchUserProps(t *testing.T, store store.Store) { | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	err = store.PatchUserProps(user.ID, patch) | ||||
| 	require.NoError(t, err) | ||||
| 	userPreferences, err := store.GetUserPreferences(user.ID) | ||||
| 	userPreferences, err := store.PatchUserPreferences(user.ID, patch) | ||||
| 	require.NoError(t, err) | ||||
| 	require.Equal(t, 3, len(userPreferences)) | ||||
|  | ||||
| @@ -293,15 +300,13 @@ func testPatchUserProps(t *testing.T, store store.Store) { | ||||
| 	} | ||||
|  | ||||
| 	// Delete a prop | ||||
| 	patch = model.UserPropPatch{ | ||||
| 	patch = model.UserPreferencesPatch{ | ||||
| 		DeletedFields: []string{ | ||||
| 			key1, | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	err = store.PatchUserProps(user.ID, patch) | ||||
| 	require.NoError(t, err) | ||||
| 	userPreferences, err = store.GetUserPreferences(user.ID) | ||||
| 	userPreferences, err = store.PatchUserPreferences(user.ID, patch) | ||||
| 	require.NoError(t, err) | ||||
|  | ||||
| 	for _, preference := range userPreferences { | ||||
| @@ -316,7 +321,7 @@ func testPatchUserProps(t *testing.T, store store.Store) { | ||||
| 	} | ||||
|  | ||||
| 	// update and delete together | ||||
| 	patch = model.UserPropPatch{ | ||||
| 	patch = model.UserPreferencesPatch{ | ||||
| 		UpdatedFields: map[string]string{ | ||||
| 			key3: "new_value_3_new_again", | ||||
| 		}, | ||||
| @@ -324,9 +329,7 @@ func testPatchUserProps(t *testing.T, store store.Store) { | ||||
| 			key2, | ||||
| 		}, | ||||
| 	} | ||||
| 	err = store.PatchUserProps(user.ID, patch) | ||||
| 	require.NoError(t, err) | ||||
| 	userPreferences, err = store.GetUserPreferences(user.ID) | ||||
| 	userPreferences, err = store.PatchUserPreferences(user.ID, patch) | ||||
| 	require.NoError(t, err) | ||||
|  | ||||
| 	for _, preference := range userPreferences { | ||||
|   | ||||
| @@ -21,8 +21,9 @@ func createTestUsers(t *testing.T, store store.Store, num int) []*model.User { | ||||
| 			Username: fmt.Sprintf("mooncake.%d", i), | ||||
| 			Email:    fmt.Sprintf("mooncake.%d@example.com", i), | ||||
| 		} | ||||
| 		err := store.CreateUser(user) | ||||
| 		newUser, err := store.CreateUser(user) | ||||
| 		require.NoError(t, err) | ||||
| 		require.NotNil(t, newUser) | ||||
|  | ||||
| 		users = append(users, user) | ||||
| 	} | ||||
|   | ||||
		Reference in New Issue
	
	Block a user