From da66599fa39ddbff96b0844fabac161e130a2bc4 Mon Sep 17 00:00:00 2001 From: hmhealey Date: Thu, 1 Oct 2015 10:56:07 -0400 Subject: Added Preferences table to store user preferences --- store/sql_preference_store.go | 125 +++++++++++++++++++++++++++ store/sql_preference_store_test.go | 170 +++++++++++++++++++++++++++++++++++++ store/sql_store.go | 30 ++++--- store/store.go | 7 ++ 4 files changed, 321 insertions(+), 11 deletions(-) create mode 100644 store/sql_preference_store.go create mode 100644 store/sql_preference_store_test.go (limited to 'store') diff --git a/store/sql_preference_store.go b/store/sql_preference_store.go new file mode 100644 index 000000000..3e3a91f61 --- /dev/null +++ b/store/sql_preference_store.go @@ -0,0 +1,125 @@ +// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved. +// See License.txt for license information. + +package store + +import ( + "github.com/mattermost/platform/model" +) + +type SqlPreferenceStore struct { + *SqlStore +} + +func NewSqlPreferenceStore(sqlStore *SqlStore) PreferenceStore { + s := &SqlPreferenceStore{sqlStore} + + for _, db := range sqlStore.GetAllConns() { + table := db.AddTableWithName(model.Preference{}, "Preferences").SetKeys(false, "UserId", "Category", "Name", "AltId") + table.ColMap("UserId").SetMaxSize(26) + table.ColMap("Category").SetMaxSize(32) + table.ColMap("Name").SetMaxSize(32) + table.ColMap("AltId").SetMaxSize(26) + table.ColMap("Value").SetMaxSize(128) + } + + return s +} + +func (s SqlPreferenceStore) UpgradeSchemaIfNeeded() { +} + +func (s SqlPreferenceStore) CreateIndexesIfNotExists() { + s.CreateIndexIfNotExists("idx_preferences_user_id", "Preferences", "UserId") + s.CreateIndexIfNotExists("idx_preferences_category", "Preferences", "Category") + s.CreateIndexIfNotExists("idx_preferences_name", "Preferences", "Name") +} + +func (s SqlPreferenceStore) Save(preference *model.Preference) StoreChannel { + storeChannel := make(StoreChannel) + + go func() { + result := StoreResult{} + + if result.Err = preference.IsValid(); result.Err != nil { + storeChannel <- result + close(storeChannel) + return + } + + if err := s.GetMaster().Insert(preference); err != nil { + if IsUniqueConstraintError(err.Error(), "UserId", "preferences_pkey") { + result.Err = model.NewAppError("SqlPreferenceStore.Save", "A preference with that user id, category, name, and alt id already exists", + "user_id="+preference.UserId+", category="+preference.Category+", name="+preference.Name+", alt_id="+preference.AltId+", "+err.Error()) + } else { + result.Err = model.NewAppError("SqlPreferenceStore.Save", "We couldn't save the preference", + "user_id="+preference.UserId+", category="+preference.Category+", name="+preference.Name+", alt_id="+preference.AltId+", "+err.Error()) + } + } else { + result.Data = preference + } + + storeChannel <- result + close(storeChannel) + }() + + return storeChannel +} + +func (s SqlPreferenceStore) Update(preference *model.Preference) StoreChannel { + storeChannel := make(StoreChannel) + + go func() { + result := StoreResult{} + + if result.Err = preference.IsValid(); result.Err != nil { + storeChannel <- result + close(storeChannel) + return + } + + if count, err := s.GetMaster().Update(preference); err != nil { + result.Err = model.NewAppError("SqlPreferenceStore.Update", "We couldn't update the preference", + "user_id="+preference.UserId+", category="+preference.Category+", name="+preference.Name+", alt_id="+preference.AltId+", "+err.Error()) + } else if count != 1 { + result.Err = model.NewAppError("SqlPreferenceStore.Update", "We couldn't update the preference", + "user_id="+preference.UserId+", category="+preference.Category+", name="+preference.Name+", alt_id="+preference.AltId) + } else { + result.Data = preference + } + + storeChannel <- result + close(storeChannel) + }() + + return storeChannel +} + +func (s SqlPreferenceStore) GetByName(userId string, category string, name string) StoreChannel { + storeChannel := make(StoreChannel) + + go func() { + result := StoreResult{} + + var preferences []*model.Preference + + if _, err := s.GetReplica().Select(&preferences, + `SELECT + * + FROM + Preferences + WHERE + UserId = :UserId + AND Category = :Category + AND Name = :Name`, map[string]interface{}{"UserId": userId, "Category": category, "Name": name}); err != nil { + result.Err = model.NewAppError("SqlPreferenceStore.GetByName", "We encounted an error while finding preferences", err.Error()) + } else { + result.Data = preferences + } + + storeChannel <- result + close(storeChannel) + }() + + return storeChannel +} diff --git a/store/sql_preference_store_test.go b/store/sql_preference_store_test.go new file mode 100644 index 000000000..d7ef26374 --- /dev/null +++ b/store/sql_preference_store_test.go @@ -0,0 +1,170 @@ +// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved. +// See License.txt for license information. + +package store + +import ( + "github.com/mattermost/platform/model" + "testing" +) + +func TestPreferenceStoreSave(t *testing.T) { + Setup() + + p1 := model.Preference{ + UserId: model.NewId(), + Category: model.PREFERENCE_CATEGORY_DIRECT_CHANNELS, + Name: model.PREFERENCE_NAME_SHOWHIDE, + AltId: model.NewId(), + } + + if err := (<-store.Preference().Save(&p1)).Err; err != nil { + t.Fatal("couldn't save preference", err) + } + + if err := (<-store.Preference().Save(&p1)).Err; err == nil { + t.Fatal("shouldn't be able to save duplicate preference") + } + + p2 := model.Preference{ + UserId: model.NewId(), + Category: model.PREFERENCE_CATEGORY_DIRECT_CHANNELS, + Name: model.PREFERENCE_NAME_SHOWHIDE, + AltId: p1.AltId, + } + + if err := (<-store.Preference().Save(&p2)).Err; err != nil { + t.Fatal("couldn't save preference with duplicate category, name, alternate id", err) + } + + p3 := model.Preference{ + UserId: p1.UserId, + Category: model.PREFERENCE_CATEGORY_TEST, + Name: model.PREFERENCE_NAME_SHOWHIDE, + AltId: p1.AltId, + } + + if err := (<-store.Preference().Save(&p3)).Err; err != nil { + t.Fatal("couldn't save preference with duplicate user id, name, alternate id", err) + } + + p4 := model.Preference{ + UserId: p1.UserId, + Category: model.PREFERENCE_CATEGORY_DIRECT_CHANNELS, + Name: model.PREFERENCE_NAME_TEST, + AltId: p1.AltId, + } + + if err := (<-store.Preference().Save(&p4)).Err; err != nil { + t.Fatal("couldn't save preference with duplicate user id, category, alternate id", err) + } + + p5 := model.Preference{ + UserId: p1.UserId, + Category: model.PREFERENCE_CATEGORY_DIRECT_CHANNELS, + Name: model.PREFERENCE_NAME_SHOWHIDE, + AltId: model.NewId(), + } + + if err := (<-store.Preference().Save(&p5)).Err; err != nil { + t.Fatal("couldn't save preference with duplicate user id, category, name", err) + } +} + +func TestPreferenceStoreUpdate(t *testing.T) { + Setup() + + id := model.NewId() + + p1 := model.Preference{ + UserId: id, + Category: model.PREFERENCE_CATEGORY_DIRECT_CHANNELS, + Name: model.PREFERENCE_NAME_SHOWHIDE, + } + Must(store.Preference().Save(&p1)) + + p1.Value = "1234garbage" + if err := (<-store.Preference().Update(&p1)).Err; err != nil { + t.Fatal(err) + } + + p1.UserId = model.NewId() + if err := (<-store.Preference().Update(&p1)).Err; err == nil { + t.Fatal("update should have failed because of changed user id") + } + + p1.UserId = id + p1.Category = model.PREFERENCE_CATEGORY_TEST + if err := (<-store.Preference().Update(&p1)).Err; err == nil { + t.Fatal("update should have failed because of changed category") + } + + p1.Category = model.PREFERENCE_CATEGORY_DIRECT_CHANNELS + p1.Name = model.PREFERENCE_NAME_TEST + if err := (<-store.Preference().Update(&p1)).Err; err == nil { + t.Fatal("update should have failed because of changed name") + } + + p1.Name = model.PREFERENCE_NAME_SHOWHIDE + p1.AltId = model.NewId() + if err := (<-store.Preference().Update(&p1)).Err; err == nil { + t.Fatal("update should have failed because of changed alternate id") + } +} + +func TestPreferenceGetByName(t *testing.T) { + Setup() + + p1 := model.Preference{ + UserId: model.NewId(), + Category: model.PREFERENCE_CATEGORY_DIRECT_CHANNELS, + Name: model.PREFERENCE_NAME_SHOWHIDE, + AltId: model.NewId(), + } + + // same user/category/name, different alt id + p2 := model.Preference{ + UserId: p1.UserId, + Category: p1.Category, + Name: p1.Name, + AltId: model.NewId(), + } + + // same user/category/alt id, different name + p3 := model.Preference{ + UserId: p1.UserId, + Category: p1.Category, + Name: model.PREFERENCE_NAME_TEST, + AltId: p1.AltId, + } + + // same user/name/alt id, different category + p4 := model.Preference{ + UserId: p1.UserId, + Category: model.PREFERENCE_CATEGORY_TEST, + Name: p1.Name, + AltId: p1.AltId, + } + + // same name/category/alt id, different user + p5 := model.Preference{ + UserId: model.NewId(), + Category: p1.Category, + Name: p1.Name, + AltId: p1.AltId, + } + + Must(store.Preference().Save(&p1)) + Must(store.Preference().Save(&p2)) + Must(store.Preference().Save(&p3)) + Must(store.Preference().Save(&p4)) + Must(store.Preference().Save(&p5)) + + if result := <-store.Preference().GetByName(p1.UserId, p1.Category, p1.Name); result.Err != nil { + t.Fatal(result.Err) + } else if data := result.Data.([]*model.Preference); len(data) != 2 { + t.Fatal("got the wrong number of preferences") + } else if !((*data[0] == p1 && *data[1] == p2) || (*data[0] == p2 && *data[1] == p1)) { + t.Fatal("got incorrect preferences") + } +} diff --git a/store/sql_store.go b/store/sql_store.go index 900543460..4b055e455 100644 --- a/store/sql_store.go +++ b/store/sql_store.go @@ -30,17 +30,18 @@ import ( ) type SqlStore struct { - master *gorp.DbMap - replicas []*gorp.DbMap - team TeamStore - channel ChannelStore - post PostStore - user UserStore - audit AuditStore - session SessionStore - oauth OAuthStore - system SystemStore - webhook WebhookStore + master *gorp.DbMap + replicas []*gorp.DbMap + team TeamStore + channel ChannelStore + post PostStore + user UserStore + audit AuditStore + session SessionStore + oauth OAuthStore + system SystemStore + webhook WebhookStore + preference PreferenceStore } func NewSqlStore() Store { @@ -93,6 +94,7 @@ func NewSqlStore() Store { sqlStore.oauth = NewSqlOAuthStore(sqlStore) sqlStore.system = NewSqlSystemStore(sqlStore) sqlStore.webhook = NewSqlWebhookStore(sqlStore) + sqlStore.preference = NewSqlPreferenceStore(sqlStore) sqlStore.master.CreateTablesIfNotExists() @@ -105,6 +107,7 @@ func NewSqlStore() Store { sqlStore.oauth.(*SqlOAuthStore).UpgradeSchemaIfNeeded() sqlStore.system.(*SqlSystemStore).UpgradeSchemaIfNeeded() sqlStore.webhook.(*SqlWebhookStore).UpgradeSchemaIfNeeded() + sqlStore.preference.(*SqlPreferenceStore).UpgradeSchemaIfNeeded() sqlStore.team.(*SqlTeamStore).CreateIndexesIfNotExists() sqlStore.channel.(*SqlChannelStore).CreateIndexesIfNotExists() @@ -115,6 +118,7 @@ func NewSqlStore() Store { sqlStore.oauth.(*SqlOAuthStore).CreateIndexesIfNotExists() sqlStore.system.(*SqlSystemStore).CreateIndexesIfNotExists() sqlStore.webhook.(*SqlWebhookStore).CreateIndexesIfNotExists() + sqlStore.preference.(*SqlPreferenceStore).CreateIndexesIfNotExists() if model.IsPreviousVersion(schemaVersion) { sqlStore.system.Update(&model.System{Name: "Version", Value: model.CurrentVersion}) @@ -472,6 +476,10 @@ func (ss SqlStore) Webhook() WebhookStore { return ss.webhook } +func (ss SqlStore) Preference() PreferenceStore { + return ss.preference +} + type mattermConverter struct{} func (me mattermConverter) ToDb(val interface{}) (interface{}, error) { diff --git a/store/store.go b/store/store.go index e539bc98a..8a8faae85 100644 --- a/store/store.go +++ b/store/store.go @@ -37,6 +37,7 @@ type Store interface { OAuth() OAuthStore System() SystemStore Webhook() WebhookStore + Preference() PreferenceStore Close() } @@ -149,3 +150,9 @@ type WebhookStore interface { GetIncomingByUser(userId string) StoreChannel DeleteIncoming(webhookId string, time int64) StoreChannel } + +type PreferenceStore interface { + Save(preference *model.Preference) StoreChannel + Update(preference *model.Preference) StoreChannel + GetByName(userId string, category string, name string) StoreChannel +} -- cgit v1.2.3-1-g7c22