Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add user settings key/value DB table #16834

Merged
merged 72 commits into from
Nov 22, 2021
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
72 commits
Select commit Hold shift + click to select a range
515311b
add user settings k/v DB table
techknowlogick Aug 27, 2021
40c45f4
resolve lint issues
techknowlogick Aug 29, 2021
7a35bc3
Merge branch 'main' into user-settings
techknowlogick Aug 29, 2021
a0a1dac
add migration & tests
techknowlogick Aug 29, 2021
d6c169c
fix lint
techknowlogick Aug 29, 2021
9614a3f
fix sql query
techknowlogick Aug 29, 2021
147a52c
update per Lunny's feedback
techknowlogick Aug 30, 2021
03f8bca
Merge branch 'main' into user-settings
techknowlogick Aug 30, 2021
0f2bd2b
Merge branch 'main' into user-settings
techknowlogick Aug 30, 2021
2132a96
Merge branch 'main' into user-settings
techknowlogick Aug 31, 2021
ebae68e
Update models/user_setting.go
techknowlogick Sep 30, 2021
9f72e70
Rename v193.go to v196.go
techknowlogick Oct 3, 2021
a3a8271
Rename v196.go to v198.go
techknowlogick Oct 3, 2021
10c4a7c
Merge remote-tracking branch 'upstream/main' into user-settings
techknowlogick Oct 3, 2021
caaf873
refactor PR from recent models/db refactor
techknowlogick Oct 3, 2021
f04598c
refactor PR from recent models/db refactor
techknowlogick Oct 3, 2021
d82b4de
use correct way to start new session
techknowlogick Oct 3, 2021
d48c711
fix tests
techknowlogick Oct 3, 2021
7ea3b8a
Merge branch 'main' into user-settings
techknowlogick Oct 3, 2021
e67e72f
Merge branch 'main' into user-settings
techknowlogick Oct 7, 2021
0314c56
Apply suggestions from code review
techknowlogick Oct 7, 2021
2890b7b
Update models/migrations/v197.go
techknowlogick Oct 7, 2021
e7bce0c
Update models/error.go
techknowlogick Oct 7, 2021
851eb3d
Update models/user_setting.go
techknowlogick Oct 7, 2021
4c56f95
update per delvh feedback
techknowlogick Oct 7, 2021
ee8104c
rename to 198
techknowlogick Oct 10, 2021
844fb46
Merge branch 'main' into user-settings
techknowlogick Oct 10, 2021
f5fe983
upsert & add const to test
techknowlogick Oct 10, 2021
70df32f
rm dead code
techknowlogick Oct 10, 2021
d18b2f9
mk fmt
techknowlogick Oct 10, 2021
d2e9d1c
Rename v198.go to v199.go
techknowlogick Oct 15, 2021
ea98352
Merge branch 'main' into user-settings
techknowlogick Oct 15, 2021
595abd0
temp move migration to a higher number to make merge conflict easier
techknowlogick Nov 8, 2021
553b583
use correct migration number
techknowlogick Nov 8, 2021
261fcbb
update per feedback
techknowlogick Nov 9, 2021
1c1d85a
swap to map
techknowlogick Nov 9, 2021
5d98572
return error when key not lower case
techknowlogick Nov 10, 2021
7401b41
Merge branch 'main' into user-settings
wxiaoguang Nov 10, 2021
cbb145c
match migration struct to user setting struct
techknowlogick Nov 10, 2021
d8aeec7
mv files
techknowlogick Nov 10, 2021
d8e1c18
fix lint
techknowlogick Nov 10, 2021
4489d39
change struct name
techknowlogick Nov 10, 2021
cb50d48
fix caps
techknowlogick Nov 10, 2021
5f719fc
add comment to placate lint
techknowlogick Nov 10, 2021
e8b2f2c
Merge branch 'main' into user-settings
techknowlogick Nov 11, 2021
ba39900
pass tests
techknowlogick Nov 11, 2021
d6c4bb1
Merge branch 'main' into user-settings
techknowlogick Nov 12, 2021
46ba3b7
Merge branch 'main' into user-settings
techknowlogick Nov 13, 2021
4af49fd
Merge branch 'main' into user-settings
techknowlogick Nov 18, 2021
ee6ad18
Merge branch 'main' into user-settings
techknowlogick Nov 19, 2021
09841e6
Update setting_test.go
techknowlogick Nov 19, 2021
23b43b5
Merge branch 'main' into user-settings
techknowlogick Nov 19, 2021
c363283
Merge branch 'main' into user-settings
techknowlogick Nov 19, 2021
a28c975
Merge branch 'main' into user-settings
techknowlogick Nov 20, 2021
da22337
Merge branch 'main' into user-settings
techknowlogick Nov 20, 2021
d39f3c6
Merge branch 'main' into user-settings
techknowlogick Nov 20, 2021
c18f5c0
Merge branch 'main' into user-settings
techknowlogick Nov 21, 2021
43b32d0
swap upsert logic
techknowlogick Nov 21, 2021
00950c6
woops, only update key for specific user
techknowlogick Nov 21, 2021
cef5e77
update per lunny feedback
techknowlogick Nov 21, 2021
374edb2
Merge branch 'main' into user-settings
techknowlogick Nov 21, 2021
63f71fe
Update setting.go
wxiaoguang Nov 21, 2021
83fa71e
Update setting_test.go
wxiaoguang Nov 21, 2021
c81f0b8
Update setting.go comments
wxiaoguang Nov 21, 2021
84aac13
Update setting.go
wxiaoguang Nov 21, 2021
ee57a24
proper casing
techknowlogick Nov 21, 2021
25fbcae
more casing changes
techknowlogick Nov 21, 2021
73a869e
fix transaction session and unit test
wxiaoguang Nov 22, 2021
5d28949
remove unnecessary SQL sorting
wxiaoguang Nov 22, 2021
162bcbe
use WithTx instead of TxContext
wxiaoguang Nov 22, 2021
8f004d1
Merge branch 'main' into user-settings
wxiaoguang Nov 22, 2021
d37c7c8
Merge branch 'main' into user-settings
lunny Nov 22, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions models/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,36 @@ func (err ErrReachLimitOfRepo) Error() string {
return fmt.Sprintf("user has reached maximum limit of repositories [limit: %d]", err.Limit)
}

// ErrUserSettingExists represents a "setting already exists for user" error.
type ErrUserSettingExists struct {
Setting *UserSetting
}

// IsErrUserSettingExists checks if an error is a ErrUserSettingExists.
func IsErrUserSettingExists(err error) bool {
_, ok := err.(ErrUserSettingExists)
return ok
}

func (err ErrUserSettingExists) Error() string {
return fmt.Sprintf("setting already exists for user [uid: %d, key: %s]", err.Setting.UserID, err.Setting.Key)
}

// ErrUserSettingNotExists represents a "setting already exists for user" error.
techknowlogick marked this conversation as resolved.
Show resolved Hide resolved
type ErrUserSettingNotExists struct {
Setting *UserSetting
}

// IsErrUserSettingNotExists checks if an error is a ErrUserSettingNotExists.
func IsErrUserSettingNotExists(err error) bool {
_, ok := err.(ErrUserSettingNotExists)
return ok
}

func (err ErrUserSettingNotExists) Error() string {
return fmt.Sprintf("setting already exists for user [uid: %d, key: %s]", err.Setting.UserID, err.Setting.Key)
}

// __ __.__ __ .__
// / \ / \__| | _|__|
// \ \/\/ / | |/ / |
Expand Down
2 changes: 2 additions & 0 deletions models/migrations/migrations.go
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,8 @@ var migrations = []Migration{
NewMigration("Add table commit_status_index", addTableCommitStatusIndex),
// v196 -> v197
NewMigration("Add Color to ProjectBoard table", addColorColToProjectBoard),
// v197 -> v198
NewMigration("Create key/value table for user settings", createUserSettingsTable),
}

// GetCurrentDBVersion returns the current db version
Expand Down
25 changes: 25 additions & 0 deletions models/migrations/v197.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package migrations

import (
"fmt"

"xorm.io/xorm"
)

func createUserSettingsTable(x *xorm.Engine) error {
type UserSetting struct {
ID int64 `xorm:"pk autoincr"`
UserID int64 `xorm:"index unique(key_userid)"` // to load all of someone's settings
Key string `xorm:"varchar(255) index unique(key_userid)"` // ensure key is always lowercase
Value string `xorm:"text"`
}
if err := x.Sync2(new(UserSetting)); err != nil {
return fmt.Errorf("sync2: %v", err)
}
return nil

}
1 change: 1 addition & 0 deletions models/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -1207,6 +1207,7 @@ func deleteUser(e db.Engine, u *User) error {
&TeamUser{UID: u.ID},
&Collaboration{UserID: u.ID},
&Stopwatch{UserID: u.ID},
&UserSetting{UserID: u.ID},
); err != nil {
return fmt.Errorf("deleteBeans: %v", err)
}
Expand Down
115 changes: 115 additions & 0 deletions models/user_setting.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
lunny marked this conversation as resolved.
Show resolved Hide resolved
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package models

import (
"strings"

"code.gitea.io/gitea/models/db"

"xorm.io/builder"
)

// UserSetting is a key value store of user settings
type UserSetting struct {
ID int64 `xorm:"pk autoincr"`
UserID int64 `xorm:"index unique(key_userid)"` // to load all of someone's settings
Key string `xorm:"varchar(255) index unique(key_userid)"` // ensure key is always lowercase
Value string `xorm:"text"`
}

// BeforeInsert will be invoked by XORM before inserting a record
func (setting *UserSetting) BeforeInsert() {
techknowlogick marked this conversation as resolved.
Show resolved Hide resolved
setting.Key = strings.ToLower(setting.Key)
}

// BeforeUpdate will be invoked by XORM before updating a record
func (setting *UserSetting) BeforeUpdate() {
techknowlogick marked this conversation as resolved.
Show resolved Hide resolved
setting.Key = strings.ToLower(setting.Key)
}

func init() {
db.RegisterModel(new(UserSetting))
}

// GetUserSetting returns specific settings from user
func GetUserSetting(uid int64, keys []string) ([]*UserSetting, error) {
wxiaoguang marked this conversation as resolved.
Show resolved Hide resolved
settings := make([]*UserSetting, 0, 5)
techknowlogick marked this conversation as resolved.
Show resolved Hide resolved
if err := db.GetEngine(db.DefaultContext).
Where("user_id=?", uid).
And(builder.In("key", keys)).
Asc("id").
techknowlogick marked this conversation as resolved.
Show resolved Hide resolved
Find(&settings); err != nil {
return nil, err
}
return settings, nil
}

// GetUserAllSettings returns all settings from user
func GetUserAllSettings(uid int64) ([]*UserSetting, error) {
techknowlogick marked this conversation as resolved.
Show resolved Hide resolved
settings := make([]*UserSetting, 0, 5)
if err := db.GetEngine(db.DefaultContext).
Where("user_id=?", uid).
Asc("id").
Find(&settings); err != nil {
return nil, err
}
return settings, nil
}

func addUserSetting(e db.Engine, setting *UserSetting) error {
used, err := settingExists(e, setting.UserID, setting.Key)
if err != nil {
return err
} else if used {
return ErrUserSettingExists{setting}
}
_, err = e.Insert(setting)
return err
}

func settingExists(e db.Engine, uid int64, key string) (bool, error) {
if len(key) == 0 {
return true, nil
}

return e.Table(&UserSetting{}).Exist(&UserSetting{UserID: uid, Key: strings.ToLower(key)})
}

// DeleteUserSetting deletes a specific setting for a user
func DeleteUserSetting(setting *UserSetting) error {
sess := db.NewSession(db.DefaultContext)
defer sess.Close()
if err := sess.Begin(); err != nil {
techknowlogick marked this conversation as resolved.
Show resolved Hide resolved
return err
}

if _, err := sess.Delete(setting); err != nil {
return err
}

return sess.Commit()
}

// SetUserSetting updates a users' setting for a specific key
func SetUserSetting(setting *UserSetting) error {
err := addUserSetting(db.GetEngine(db.DefaultContext), setting)
if err != nil && IsErrUserSettingExists(err) {
return updateUserSettingValue(db.GetEngine(db.DefaultContext), setting)
}
return err
}

func updateUserSettingValue(e db.Engine, setting *UserSetting) error {
used, err := settingExists(e, setting.UserID, setting.Key)
if err != nil {
return err
techknowlogick marked this conversation as resolved.
Show resolved Hide resolved
} else if !used {
return ErrUserSettingNotExists{setting}
}

_, err = e.ID(setting.ID).Cols("value").Update(setting)
return err
}
44 changes: 44 additions & 0 deletions models/user_setting_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package models

import (
"testing"

"code.gitea.io/gitea/models/db"

"github.com/stretchr/testify/assert"
)

func TestUserSettings(t *testing.T) {
assert.NoError(t, db.PrepareTestDatabase())

newSetting := &UserSetting{UserID: 99, Key: "test_user_setting", Value: "Gitea User Setting Test"}
techknowlogick marked this conversation as resolved.
Show resolved Hide resolved

// create setting
err := SetUserSetting(newSetting)
assert.NoError(t, err)

// get specific setting
userSettings, err := GetUserSetting(99, []string{"test_user_setting"})
assert.NoError(t, err)
assert.Len(t, userSettings, 1)
assert.EqualValues(t, newSetting.Value, userSettings[0].Value)

// updated setting
updatedSetting := &UserSetting{UserID: 99, Key: "test_user_setting", Value: "Updated", ID: userSettings[0].ID}
err = SetUserSetting(updatedSetting)
assert.NoError(t, err)

// get all settings
userSettings, err = GetUserAllSettings(99)
assert.NoError(t, err)
assert.Len(t, userSettings, 1)
assert.EqualValues(t, userSettings[0].Value, updatedSetting.Value)

// delete setting
err = DeleteUserSetting(updatedSetting)
assert.NoError(t, err)
}