From 001a4448ca5fb0018eeb442915b473b121c04bf3 Mon Sep 17 00:00:00 2001 From: =Corey Hulen Date: Wed, 6 Jan 2016 21:09:05 -0600 Subject: PLT-1429 adding sql storage for slash commands --- store/sql_command_store.go | 174 ++++++++++++++++++++++++++++++++++++++++ store/sql_command_store_test.go | 155 +++++++++++++++++++++++++++++++++++ store/sql_store.go | 8 ++ store/store.go | 10 +++ 4 files changed, 347 insertions(+) create mode 100644 store/sql_command_store.go create mode 100644 store/sql_command_store_test.go (limited to 'store') diff --git a/store/sql_command_store.go b/store/sql_command_store.go new file mode 100644 index 000000000..cb817d8f8 --- /dev/null +++ b/store/sql_command_store.go @@ -0,0 +1,174 @@ +// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package store + +import ( + "github.com/mattermost/platform/model" +) + +type SqlCommandStore struct { + *SqlStore +} + +func NewSqlCommandStore(sqlStore *SqlStore) CommandStore { + s := &SqlCommandStore{sqlStore} + + for _, db := range sqlStore.GetAllConns() { + tableo := db.AddTableWithName(model.Command{}, "Commands").SetKeys(false, "Id") + tableo.ColMap("Id").SetMaxSize(26) + tableo.ColMap("Token").SetMaxSize(26) + tableo.ColMap("CreatorId").SetMaxSize(26) + tableo.ColMap("TeamId").SetMaxSize(26) + tableo.ColMap("Trigger").SetMaxSize(128) + tableo.ColMap("URL").SetMaxSize(1024) + tableo.ColMap("Method").SetMaxSize(1) + tableo.ColMap("Username").SetMaxSize(64) + tableo.ColMap("IconURL").SetMaxSize(1024) + tableo.ColMap("AutoCompleteDesc").SetMaxSize(1024) + tableo.ColMap("AutoCompleteHint").SetMaxSize(1024) + tableo.ColMap("DisplayName").SetMaxSize(64) + } + + return s +} + +func (s SqlCommandStore) UpgradeSchemaIfNeeded() { +} + +func (s SqlCommandStore) CreateIndexesIfNotExists() { + s.CreateIndexIfNotExists("idx_command_team_id", "Commands", "TeamId") +} + +func (s SqlCommandStore) Save(command *model.Command) StoreChannel { + storeChannel := make(StoreChannel) + + go func() { + result := StoreResult{} + + if len(command.Id) > 0 { + result.Err = model.NewAppError("SqlCommandStore.Save", + "You cannot overwrite an existing Command", "id="+command.Id) + storeChannel <- result + close(storeChannel) + return + } + + command.PreSave() + if result.Err = command.IsValid(); result.Err != nil { + storeChannel <- result + close(storeChannel) + return + } + + if err := s.GetMaster().Insert(command); err != nil { + result.Err = model.NewAppError("SqlCommandStore.Save", "We couldn't save the Command", "id="+command.Id+", "+err.Error()) + } else { + result.Data = command + } + + storeChannel <- result + close(storeChannel) + }() + + return storeChannel +} + +func (s SqlCommandStore) Get(id string) StoreChannel { + storeChannel := make(StoreChannel) + + go func() { + result := StoreResult{} + + var command model.Command + + if err := s.GetReplica().SelectOne(&command, "SELECT * FROM Commands WHERE Id = :Id AND DeleteAt = 0", map[string]interface{}{"Id": id}); err != nil { + result.Err = model.NewAppError("SqlCommandStore.Get", "We couldn't get the command", "id="+id+", err="+err.Error()) + } + + result.Data = &command + + storeChannel <- result + close(storeChannel) + }() + + return storeChannel +} + +func (s SqlCommandStore) GetByTeam(teamId string) StoreChannel { + storeChannel := make(StoreChannel) + + go func() { + result := StoreResult{} + + var commands []*model.Command + + if _, err := s.GetReplica().Select(&commands, "SELECT * FROM Commands WHERE TeamId = :TeamId AND DeleteAt = 0", map[string]interface{}{"TeamId": teamId}); err != nil { + result.Err = model.NewAppError("SqlCommandStore.GetByTeam", "We couldn't get the commands", "teamId="+teamId+", err="+err.Error()) + } + + result.Data = commands + + storeChannel <- result + close(storeChannel) + }() + + return storeChannel +} + +func (s SqlCommandStore) Delete(commandId string, time int64) StoreChannel { + storeChannel := make(StoreChannel) + + go func() { + result := StoreResult{} + + _, err := s.GetMaster().Exec("Update Commands SET DeleteAt = :DeleteAt, UpdateAt = :UpdateAt WHERE Id = :Id", map[string]interface{}{"DeleteAt": time, "UpdateAt": time, "Id": commandId}) + if err != nil { + result.Err = model.NewAppError("SqlCommandStore.Delete", "We couldn't delete the command", "id="+commandId+", err="+err.Error()) + } + + storeChannel <- result + close(storeChannel) + }() + + return storeChannel +} + +func (s SqlCommandStore) PermanentDeleteByUser(userId string) StoreChannel { + storeChannel := make(StoreChannel) + + go func() { + result := StoreResult{} + + _, err := s.GetMaster().Exec("DELETE FROM Commands WHERE CreatorId = :UserId", map[string]interface{}{"UserId": userId}) + if err != nil { + result.Err = model.NewAppError("SqlCommandStore.DeleteByUser", "We couldn't delete the command", "id="+userId+", err="+err.Error()) + } + + storeChannel <- result + close(storeChannel) + }() + + return storeChannel +} + +func (s SqlCommandStore) Update(hook *model.Command) StoreChannel { + storeChannel := make(StoreChannel) + + go func() { + result := StoreResult{} + + hook.UpdateAt = model.GetMillis() + + if _, err := s.GetMaster().Update(hook); err != nil { + result.Err = model.NewAppError("SqlCommandStore.Update", "We couldn't update the command", "id="+hook.Id+", "+err.Error()) + } else { + result.Data = hook + } + + storeChannel <- result + close(storeChannel) + }() + + return storeChannel +} diff --git a/store/sql_command_store_test.go b/store/sql_command_store_test.go new file mode 100644 index 000000000..b4610d4aa --- /dev/null +++ b/store/sql_command_store_test.go @@ -0,0 +1,155 @@ +// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package store + +import ( + "github.com/mattermost/platform/model" + "testing" +) + +func TestCommandStoreSave(t *testing.T) { + Setup() + + o1 := model.Command{} + o1.CreatorId = model.NewId() + o1.Method = model.COMMAND_METHOD_POST + o1.TeamId = model.NewId() + o1.URL = "http://nowhere.com/" + + if err := (<-store.Command().Save(&o1)).Err; err != nil { + t.Fatal("couldn't save item", err) + } + + if err := (<-store.Command().Save(&o1)).Err; err == nil { + t.Fatal("shouldn't be able to update from save") + } +} + +func TestCommandStoreGet(t *testing.T) { + Setup() + + o1 := &model.Command{} + o1.CreatorId = model.NewId() + o1.Method = model.COMMAND_METHOD_POST + o1.TeamId = model.NewId() + o1.URL = "http://nowhere.com/" + + o1 = (<-store.Command().Save(o1)).Data.(*model.Command) + + if r1 := <-store.Command().Get(o1.Id); r1.Err != nil { + t.Fatal(r1.Err) + } else { + if r1.Data.(*model.Command).CreateAt != o1.CreateAt { + t.Fatal("invalid returned command") + } + } + + if err := (<-store.Command().Get("123")).Err; err == nil { + t.Fatal("Missing id should have failed") + } +} + +func TestCommandStoreGetByTeam(t *testing.T) { + Setup() + + o1 := &model.Command{} + o1.CreatorId = model.NewId() + o1.Method = model.COMMAND_METHOD_POST + o1.TeamId = model.NewId() + o1.URL = "http://nowhere.com/" + + o1 = (<-store.Command().Save(o1)).Data.(*model.Command) + + if r1 := <-store.Command().GetByTeam(o1.TeamId); r1.Err != nil { + t.Fatal(r1.Err) + } else { + if r1.Data.([]*model.Command)[0].CreateAt != o1.CreateAt { + t.Fatal("invalid returned command") + } + } + + if result := <-store.Command().GetByTeam("123"); result.Err != nil { + t.Fatal(result.Err) + } else { + if len(result.Data.([]*model.Command)) != 0 { + t.Fatal("no commands should have returned") + } + } +} + +func TestCommandStoreDelete(t *testing.T) { + Setup() + + o1 := &model.Command{} + o1.CreatorId = model.NewId() + o1.Method = model.COMMAND_METHOD_POST + o1.TeamId = model.NewId() + o1.URL = "http://nowhere.com/" + + o1 = (<-store.Command().Save(o1)).Data.(*model.Command) + + if r1 := <-store.Command().Get(o1.Id); r1.Err != nil { + t.Fatal(r1.Err) + } else { + if r1.Data.(*model.Command).CreateAt != o1.CreateAt { + t.Fatal("invalid returned command") + } + } + + if r2 := <-store.Command().Delete(o1.Id, model.GetMillis()); r2.Err != nil { + t.Fatal(r2.Err) + } + + if r3 := (<-store.Command().Get(o1.Id)); r3.Err == nil { + t.Log(r3.Data) + t.Fatal("Missing id should have failed") + } +} + +func TestCommandStoreDeleteByUser(t *testing.T) { + Setup() + + o1 := &model.Command{} + o1.CreatorId = model.NewId() + o1.Method = model.COMMAND_METHOD_POST + o1.TeamId = model.NewId() + o1.URL = "http://nowhere.com/" + + o1 = (<-store.Command().Save(o1)).Data.(*model.Command) + + if r1 := <-store.Command().Get(o1.Id); r1.Err != nil { + t.Fatal(r1.Err) + } else { + if r1.Data.(*model.Command).CreateAt != o1.CreateAt { + t.Fatal("invalid returned command") + } + } + + if r2 := <-store.Command().PermanentDeleteByUser(o1.CreatorId); r2.Err != nil { + t.Fatal(r2.Err) + } + + if r3 := (<-store.Command().Get(o1.Id)); r3.Err == nil { + t.Log(r3.Data) + t.Fatal("Missing id should have failed") + } +} + +func TestCommandStoreUpdate(t *testing.T) { + Setup() + + o1 := &model.Command{} + o1.CreatorId = model.NewId() + o1.Method = model.COMMAND_METHOD_POST + o1.TeamId = model.NewId() + o1.URL = "http://nowhere.com/" + + o1 = (<-store.Command().Save(o1)).Data.(*model.Command) + + o1.Token = model.NewId() + + if r2 := <-store.Command().Update(o1); r2.Err != nil { + t.Fatal(r2.Err) + } +} diff --git a/store/sql_store.go b/store/sql_store.go index d17a3e8c3..30a464586 100644 --- a/store/sql_store.go +++ b/store/sql_store.go @@ -47,6 +47,7 @@ type SqlStore struct { oauth OAuthStore system SystemStore webhook WebhookStore + command CommandStore preference PreferenceStore } @@ -119,6 +120,7 @@ func NewSqlStore() Store { sqlStore.oauth = NewSqlOAuthStore(sqlStore) sqlStore.system = NewSqlSystemStore(sqlStore) sqlStore.webhook = NewSqlWebhookStore(sqlStore) + sqlStore.command = NewSqlCommandStore(sqlStore) sqlStore.preference = NewSqlPreferenceStore(sqlStore) err := sqlStore.master.CreateTablesIfNotExists() @@ -135,6 +137,7 @@ func NewSqlStore() Store { sqlStore.oauth.(*SqlOAuthStore).UpgradeSchemaIfNeeded() sqlStore.system.(*SqlSystemStore).UpgradeSchemaIfNeeded() sqlStore.webhook.(*SqlWebhookStore).UpgradeSchemaIfNeeded() + sqlStore.command.(*SqlCommandStore).UpgradeSchemaIfNeeded() sqlStore.preference.(*SqlPreferenceStore).UpgradeSchemaIfNeeded() sqlStore.team.(*SqlTeamStore).CreateIndexesIfNotExists() @@ -146,6 +149,7 @@ func NewSqlStore() Store { sqlStore.oauth.(*SqlOAuthStore).CreateIndexesIfNotExists() sqlStore.system.(*SqlSystemStore).CreateIndexesIfNotExists() sqlStore.webhook.(*SqlWebhookStore).CreateIndexesIfNotExists() + sqlStore.command.(*SqlCommandStore).CreateIndexesIfNotExists() sqlStore.preference.(*SqlPreferenceStore).CreateIndexesIfNotExists() sqlStore.preference.(*SqlPreferenceStore).DeleteUnusedFeatures() @@ -530,6 +534,10 @@ func (ss SqlStore) Webhook() WebhookStore { return ss.webhook } +func (ss SqlStore) Command() CommandStore { + return ss.command +} + func (ss SqlStore) Preference() PreferenceStore { return ss.preference } diff --git a/store/store.go b/store/store.go index 8e03c8ee7..b19bba826 100644 --- a/store/store.go +++ b/store/store.go @@ -37,6 +37,7 @@ type Store interface { OAuth() OAuthStore System() SystemStore Webhook() WebhookStore + Command() CommandStore Preference() PreferenceStore MarkSystemRanUnitTests() Close() @@ -182,6 +183,15 @@ type WebhookStore interface { UpdateOutgoing(hook *model.OutgoingWebhook) StoreChannel } +type CommandStore interface { + Save(webhook *model.Command) StoreChannel + Get(id string) StoreChannel + GetByTeam(teamId string) StoreChannel + Delete(commandId string, time int64) StoreChannel + PermanentDeleteByUser(userId string) StoreChannel + Update(hook *model.Command) StoreChannel +} + type PreferenceStore interface { Save(preferences *model.Preferences) StoreChannel Get(userId string, category string, name string) StoreChannel -- cgit v1.2.3-1-g7c22