diff options
Diffstat (limited to 'store')
-rw-r--r-- | store/layered_store.go | 4 | ||||
-rw-r--r-- | store/sql_command_webhook_store.go | 125 | ||||
-rw-r--r-- | store/sql_command_webhook_store_test.go | 65 | ||||
-rw-r--r-- | store/sql_store.go | 1 | ||||
-rw-r--r-- | store/sql_supplier.go | 7 | ||||
-rw-r--r-- | store/store.go | 8 |
6 files changed, 210 insertions, 0 deletions
diff --git a/store/layered_store.go b/store/layered_store.go index 4eb908659..d215cb2f0 100644 --- a/store/layered_store.go +++ b/store/layered_store.go @@ -107,6 +107,10 @@ func (s *LayeredStore) Command() CommandStore { return s.DatabaseLayer.Command() } +func (s *LayeredStore) CommandWebhook() CommandWebhookStore { + return s.DatabaseLayer.CommandWebhook() +} + func (s *LayeredStore) Preference() PreferenceStore { return s.DatabaseLayer.Preference() } diff --git a/store/sql_command_webhook_store.go b/store/sql_command_webhook_store.go new file mode 100644 index 000000000..af4b298b1 --- /dev/null +++ b/store/sql_command_webhook_store.go @@ -0,0 +1,125 @@ +// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package store + +import ( + "net/http" + + "database/sql" + + l4g "github.com/alecthomas/log4go" + + "github.com/mattermost/platform/model" +) + +type SqlCommandWebhookStore struct { + SqlStore +} + +func NewSqlCommandWebhookStore(sqlStore SqlStore) CommandWebhookStore { + s := &SqlCommandWebhookStore{sqlStore} + + for _, db := range sqlStore.GetAllConns() { + tablec := db.AddTableWithName(model.CommandWebhook{}, "CommandWebhooks").SetKeys(false, "Id") + tablec.ColMap("Id").SetMaxSize(26) + tablec.ColMap("CommandId").SetMaxSize(26) + tablec.ColMap("UserId").SetMaxSize(26) + tablec.ColMap("ChannelId").SetMaxSize(26) + tablec.ColMap("RootId").SetMaxSize(26) + tablec.ColMap("ParentId").SetMaxSize(26) + } + + return s +} + +func (s SqlCommandWebhookStore) CreateIndexesIfNotExists() { + s.CreateIndexIfNotExists("idx_command_webhook_create_at", "CommandWebhooks", "CreateAt") +} + +func (s SqlCommandWebhookStore) Save(webhook *model.CommandWebhook) StoreChannel { + storeChannel := make(StoreChannel, 1) + + go func() { + result := StoreResult{} + + if len(webhook.Id) > 0 { + result.Err = model.NewLocAppError("SqlCommandWebhookStore.Save", "store.sql_command_webhooks.save.existing.app_error", nil, "id="+webhook.Id) + storeChannel <- result + close(storeChannel) + return + } + + webhook.PreSave() + if result.Err = webhook.IsValid(); result.Err != nil { + storeChannel <- result + close(storeChannel) + return + } + + if err := s.GetMaster().Insert(webhook); err != nil { + result.Err = model.NewLocAppError("SqlCommandWebhookStore.Save", "store.sql_command_webhooks.save.app_error", nil, "id="+webhook.Id+", "+err.Error()) + } else { + result.Data = webhook + } + + storeChannel <- result + close(storeChannel) + }() + + return storeChannel +} + +func (s SqlCommandWebhookStore) Get(id string) StoreChannel { + storeChannel := make(StoreChannel, 1) + + go func() { + result := StoreResult{} + + var webhook model.CommandWebhook + + exptime := model.GetMillis() - model.COMMAND_WEBHOOK_LIFETIME + if err := s.GetReplica().SelectOne(&webhook, "SELECT * FROM CommandWebhooks WHERE Id = :Id AND CreateAt > :ExpTime", map[string]interface{}{"Id": id, "ExpTime": exptime}); err != nil { + result.Err = model.NewLocAppError("SqlCommandWebhookStore.Get", "store.sql_command_webhooks.get.app_error", nil, "id="+id+", err="+err.Error()) + if err == sql.ErrNoRows { + result.Err.StatusCode = http.StatusNotFound + } + } + + result.Data = &webhook + + storeChannel <- result + close(storeChannel) + }() + + return storeChannel +} + +func (s SqlCommandWebhookStore) TryUse(id string, limit int) StoreChannel { + storeChannel := make(StoreChannel, 1) + + go func() { + result := StoreResult{} + + if sqlResult, err := s.GetMaster().Exec("UPDATE CommandWebhooks SET UseCount = UseCount + 1 WHERE Id = :Id AND UseCount < :UseLimit", map[string]interface{}{"Id": id, "UseLimit": limit}); err != nil { + result.Err = model.NewLocAppError("SqlCommandWebhookStore.TryUse", "store.sql_command_webhooks.try_use.app_error", nil, "id="+id+", err="+err.Error()) + } else if rows, _ := sqlResult.RowsAffected(); rows == 0 { + result.Err = model.NewAppError("SqlCommandWebhookStore.TryUse", "store.sql_command_webhooks.try_use.invalid.app_error", nil, "id="+id, http.StatusBadRequest) + } + + result.Data = id + + storeChannel <- result + close(storeChannel) + }() + + return storeChannel +} + +func (s SqlCommandWebhookStore) Cleanup() { + l4g.Debug("Cleaning up command webhook store.") + exptime := model.GetMillis() - model.COMMAND_WEBHOOK_LIFETIME + if _, err := s.GetMaster().Exec("DELETE FROM CommandWebhooks WHERE CreateAt < :ExpTime", map[string]interface{}{"ExpTime": exptime}); err != nil { + l4g.Error("Unable to cleanup command webhook store.") + } +} diff --git a/store/sql_command_webhook_store_test.go b/store/sql_command_webhook_store_test.go new file mode 100644 index 000000000..2215a4263 --- /dev/null +++ b/store/sql_command_webhook_store_test.go @@ -0,0 +1,65 @@ +// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package store + +import ( + "testing" + + "net/http" + + "github.com/mattermost/platform/model" +) + +func TestCommandWebhookStore(t *testing.T) { + Setup() + + cws := store.CommandWebhook() + + h1 := &model.CommandWebhook{} + h1.CommandId = model.NewId() + h1.UserId = model.NewId() + h1.ChannelId = model.NewId() + h1 = (<-cws.Save(h1)).Data.(*model.CommandWebhook) + + if r1 := <-cws.Get(h1.Id); r1.Err != nil { + t.Fatal(r1.Err) + } else { + if *r1.Data.(*model.CommandWebhook) != *h1 { + t.Fatal("invalid returned webhook") + } + } + + if err := (<-cws.Get("123")).Err; err.StatusCode != http.StatusNotFound { + t.Fatal("Should have set the status as not found for missing id") + } + + h2 := &model.CommandWebhook{} + h2.CreateAt = model.GetMillis() - 2*model.COMMAND_WEBHOOK_LIFETIME + h2.CommandId = model.NewId() + h2.UserId = model.NewId() + h2.ChannelId = model.NewId() + h2 = (<-cws.Save(h2)).Data.(*model.CommandWebhook) + + if err := (<-cws.Get(h2.Id)).Err; err == nil || err.StatusCode != http.StatusNotFound { + t.Fatal("Should have set the status as not found for expired webhook") + } + + cws.Cleanup() + + if err := (<-cws.Get(h1.Id)).Err; err != nil { + t.Fatal("Should have no error getting unexpired webhook") + } + + if err := (<-cws.Get(h2.Id)).Err; err.StatusCode != http.StatusNotFound { + t.Fatal("Should have set the status as not found for expired webhook") + } + + if err := (<-cws.TryUse(h1.Id, 1)).Err; err != nil { + t.Fatal("Should be able to use webhook once") + } + + if err := (<-cws.TryUse(h1.Id, 1)).Err; err == nil || err.StatusCode != http.StatusBadRequest { + t.Fatal("Should be able to use webhook once") + } +} diff --git a/store/sql_store.go b/store/sql_store.go index 817f3fb0f..488b44522 100644 --- a/store/sql_store.go +++ b/store/sql_store.go @@ -72,6 +72,7 @@ type SqlStore interface { System() SystemStore Webhook() WebhookStore Command() CommandStore + CommandWebhook() CommandWebhookStore Preference() PreferenceStore License() LicenseStore Token() TokenStore diff --git a/store/sql_supplier.go b/store/sql_supplier.go index 5997a1339..5b9c268bb 100644 --- a/store/sql_supplier.go +++ b/store/sql_supplier.go @@ -70,6 +70,7 @@ type SqlSupplierOldStores struct { system SystemStore webhook WebhookStore command CommandStore + commandWebhook CommandWebhookStore preference PreferenceStore license LicenseStore token TokenStore @@ -111,6 +112,7 @@ func NewSqlSupplier() *SqlSupplier { supplier.oldStores.system = NewSqlSystemStore(supplier) supplier.oldStores.webhook = NewSqlWebhookStore(supplier) supplier.oldStores.command = NewSqlCommandStore(supplier) + supplier.oldStores.commandWebhook = NewSqlCommandWebhookStore(supplier) supplier.oldStores.preference = NewSqlPreferenceStore(supplier) supplier.oldStores.license = NewSqlLicenseStore(supplier) supplier.oldStores.token = NewSqlTokenStore(supplier) @@ -142,6 +144,7 @@ func NewSqlSupplier() *SqlSupplier { supplier.oldStores.system.(*SqlSystemStore).CreateIndexesIfNotExists() supplier.oldStores.webhook.(*SqlWebhookStore).CreateIndexesIfNotExists() supplier.oldStores.command.(*SqlCommandStore).CreateIndexesIfNotExists() + supplier.oldStores.commandWebhook.(*SqlCommandWebhookStore).CreateIndexesIfNotExists() supplier.oldStores.preference.(*SqlPreferenceStore).CreateIndexesIfNotExists() supplier.oldStores.license.(*SqlLicenseStore).CreateIndexesIfNotExists() supplier.oldStores.token.(*SqlTokenStore).CreateIndexesIfNotExists() @@ -732,6 +735,10 @@ func (ss *SqlSupplier) Command() CommandStore { return ss.oldStores.command } +func (ss *SqlSupplier) CommandWebhook() CommandWebhookStore { + return ss.oldStores.commandWebhook +} + func (ss *SqlSupplier) Preference() PreferenceStore { return ss.oldStores.preference } diff --git a/store/store.go b/store/store.go index d883ea5a2..e86b5f116 100644 --- a/store/store.go +++ b/store/store.go @@ -41,6 +41,7 @@ type Store interface { System() SystemStore Webhook() WebhookStore Command() CommandStore + CommandWebhook() CommandWebhookStore Preference() PreferenceStore License() LicenseStore Token() TokenStore @@ -326,6 +327,13 @@ type CommandStore interface { AnalyticsCommandCount(teamId string) StoreChannel } +type CommandWebhookStore interface { + Save(webhook *model.CommandWebhook) StoreChannel + Get(id string) StoreChannel + TryUse(id string, limit int) StoreChannel + Cleanup() +} + type PreferenceStore interface { Save(preferences *model.Preferences) StoreChannel Get(userId string, category string, name string) StoreChannel |