From 98186e5018bbc604796d4f9762c93f4f75e2913f Mon Sep 17 00:00:00 2001 From: JoramWilander Date: Mon, 21 Sep 2015 14:22:23 -0400 Subject: Implement incoming webhooks. --- store/sql_store.go | 8 +++ store/sql_webhook_store.go | 128 ++++++++++++++++++++++++++++++++++++++++ store/sql_webhook_store_test.go | 77 ++++++++++++++++++++++++ store/store.go | 8 +++ 4 files changed, 221 insertions(+) create mode 100644 store/sql_webhook_store.go create mode 100644 store/sql_webhook_store_test.go (limited to 'store') diff --git a/store/sql_store.go b/store/sql_store.go index adac47b4d..98703841a 100644 --- a/store/sql_store.go +++ b/store/sql_store.go @@ -40,6 +40,7 @@ type SqlStore struct { session SessionStore oauth OAuthStore system SystemStore + webhook WebhookStore } func NewSqlStore() Store { @@ -91,6 +92,7 @@ func NewSqlStore() Store { sqlStore.session = NewSqlSessionStore(sqlStore) sqlStore.oauth = NewSqlOAuthStore(sqlStore) sqlStore.system = NewSqlSystemStore(sqlStore) + sqlStore.webhook = NewSqlWebhookStore(sqlStore) sqlStore.master.CreateTablesIfNotExists() @@ -102,6 +104,7 @@ func NewSqlStore() Store { sqlStore.session.(*SqlSessionStore).UpgradeSchemaIfNeeded() sqlStore.oauth.(*SqlOAuthStore).UpgradeSchemaIfNeeded() sqlStore.system.(*SqlSystemStore).UpgradeSchemaIfNeeded() + sqlStore.webhook.(*SqlWebhookStore).UpgradeSchemaIfNeeded() sqlStore.team.(*SqlTeamStore).CreateIndexesIfNotExists() sqlStore.channel.(*SqlChannelStore).CreateIndexesIfNotExists() @@ -111,6 +114,7 @@ func NewSqlStore() Store { sqlStore.session.(*SqlSessionStore).CreateIndexesIfNotExists() sqlStore.oauth.(*SqlOAuthStore).CreateIndexesIfNotExists() sqlStore.system.(*SqlSystemStore).CreateIndexesIfNotExists() + sqlStore.webhook.(*SqlWebhookStore).CreateIndexesIfNotExists() if model.IsPreviousVersion(schemaVersion) { sqlStore.system.Update(&model.System{Name: "Version", Value: model.CurrentVersion}) @@ -469,6 +473,10 @@ func (ss SqlStore) System() SystemStore { return ss.system } +func (ss SqlStore) Webhook() WebhookStore { + return ss.webhook +} + type mattermConverter struct{} func (me mattermConverter) ToDb(val interface{}) (interface{}, error) { diff --git a/store/sql_webhook_store.go b/store/sql_webhook_store.go new file mode 100644 index 000000000..e309f79e4 --- /dev/null +++ b/store/sql_webhook_store.go @@ -0,0 +1,128 @@ +// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved. +// See License.txt for license information. + +package store + +import ( + "github.com/mattermost/platform/model" +) + +type SqlWebhookStore struct { + *SqlStore +} + +func NewSqlWebhookStore(sqlStore *SqlStore) WebhookStore { + s := &SqlWebhookStore{sqlStore} + + for _, db := range sqlStore.GetAllConns() { + table := db.AddTableWithName(model.IncomingWebhook{}, "IncomingWebhooks").SetKeys(false, "Id") + table.ColMap("Id").SetMaxSize(26) + table.ColMap("UserId").SetMaxSize(26) + table.ColMap("ChannelId").SetMaxSize(26) + table.ColMap("TeamId").SetMaxSize(26) + } + + return s +} + +func (s SqlWebhookStore) UpgradeSchemaIfNeeded() { +} + +func (s SqlWebhookStore) CreateIndexesIfNotExists() { + s.CreateIndexIfNotExists("idx_webhook_user_id", "IncomingWebhooks", "UserId") + s.CreateIndexIfNotExists("idx_webhook_team_id", "IncomingWebhooks", "TeamId") +} + +func (s SqlWebhookStore) SaveIncoming(webhook *model.IncomingWebhook) StoreChannel { + storeChannel := make(StoreChannel) + + go func() { + result := StoreResult{} + + if len(webhook.Id) > 0 { + result.Err = model.NewAppError("SqlWebhookStore.SaveIncoming", + "You cannot overwrite an existing IncomingWebhook", "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.NewAppError("SqlWebhookStore.SaveIncoming", "We couldn't save the IncomingWebhook", "id="+webhook.Id+", "+err.Error()) + } else { + result.Data = webhook + } + + storeChannel <- result + close(storeChannel) + }() + + return storeChannel +} + +func (s SqlWebhookStore) GetIncoming(id string) StoreChannel { + storeChannel := make(StoreChannel) + + go func() { + result := StoreResult{} + + var webhook model.IncomingWebhook + + if err := s.GetReplica().SelectOne(&webhook, "SELECT * FROM IncomingWebhooks WHERE Id = :Id AND DeleteAt = 0", map[string]interface{}{"Id": id}); err != nil { + result.Err = model.NewAppError("SqlWebhookStore.GetIncoming", "We couldn't get the webhook", "id="+id+", err="+err.Error()) + } + + result.Data = &webhook + + storeChannel <- result + close(storeChannel) + }() + + return storeChannel +} + +func (s SqlWebhookStore) DeleteIncoming(webhookId string, time int64) StoreChannel { + storeChannel := make(StoreChannel) + + go func() { + result := StoreResult{} + + _, err := s.GetMaster().Exec("Update IncomingWebhooks SET DeleteAt = :DeleteAt, UpdateAt = :UpdateAt WHERE Id = :Id", map[string]interface{}{"DeleteAt": time, "UpdateAt": time, "Id": webhookId}) + if err != nil { + result.Err = model.NewAppError("SqlWebhookStore.DeleteIncoming", "We couldn't delete the webhook", "id="+webhookId+", err="+err.Error()) + } + + storeChannel <- result + close(storeChannel) + }() + + return storeChannel +} + +func (s SqlWebhookStore) GetIncomingByUser(userId string) StoreChannel { + storeChannel := make(StoreChannel) + + go func() { + result := StoreResult{} + + var webhooks []*model.IncomingWebhook + + if _, err := s.GetReplica().Select(&webhooks, "SELECT * FROM IncomingWebhooks WHERE UserId = :UserId AND DeleteAt = 0", map[string]interface{}{"UserId": userId}); err != nil { + result.Err = model.NewAppError("SqlWebhookStore.GetIncomingByUser", "We couldn't get the webhook", "userId="+userId+", err="+err.Error()) + } + + result.Data = webhooks + + storeChannel <- result + close(storeChannel) + }() + + return storeChannel +} diff --git a/store/sql_webhook_store_test.go b/store/sql_webhook_store_test.go new file mode 100644 index 000000000..0a015eaf9 --- /dev/null +++ b/store/sql_webhook_store_test.go @@ -0,0 +1,77 @@ +// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved. +// See License.txt for license information. + +package store + +import ( + "github.com/mattermost/platform/model" + "testing" +) + +func TestIncomingWebhookStoreSaveIncoming(t *testing.T) { + Setup() + + o1 := model.IncomingWebhook{} + o1.ChannelId = model.NewId() + o1.UserId = model.NewId() + o1.TeamId = model.NewId() + + if err := (<-store.Webhook().SaveIncoming(&o1)).Err; err != nil { + t.Fatal("couldn't save item", err) + } + + if err := (<-store.Webhook().SaveIncoming(&o1)).Err; err == nil { + t.Fatal("shouldn't be able to update from save") + } +} + +func TestIncomingWebhookStoreGetIncoming(t *testing.T) { + Setup() + + o1 := &model.IncomingWebhook{} + o1.ChannelId = model.NewId() + o1.UserId = model.NewId() + o1.TeamId = model.NewId() + + o1 = (<-store.Webhook().SaveIncoming(o1)).Data.(*model.IncomingWebhook) + + if r1 := <-store.Webhook().GetIncoming(o1.Id); r1.Err != nil { + t.Fatal(r1.Err) + } else { + if r1.Data.(*model.IncomingWebhook).CreateAt != o1.CreateAt { + t.Fatal("invalid returned webhook") + } + } + + if err := (<-store.Webhook().GetIncoming("123")).Err; err == nil { + t.Fatal("Missing id should have failed") + } +} + +func TestIncomingWebhookStoreDelete(t *testing.T) { + Setup() + + o1 := &model.IncomingWebhook{} + o1.ChannelId = model.NewId() + o1.UserId = model.NewId() + o1.TeamId = model.NewId() + + o1 = (<-store.Webhook().SaveIncoming(o1)).Data.(*model.IncomingWebhook) + + if r1 := <-store.Webhook().GetIncoming(o1.Id); r1.Err != nil { + t.Fatal(r1.Err) + } else { + if r1.Data.(*model.IncomingWebhook).CreateAt != o1.CreateAt { + t.Fatal("invalid returned webhook") + } + } + + if r2 := <-store.Webhook().DeleteIncoming(o1.Id, model.GetMillis()); r2.Err != nil { + t.Fatal(r2.Err) + } + + if r3 := (<-store.Webhook().GetIncoming(o1.Id)); r3.Err == nil { + t.Log(r3.Data) + t.Fatal("Missing id should have failed") + } +} diff --git a/store/store.go b/store/store.go index 1344c4ebe..c9d40cfa5 100644 --- a/store/store.go +++ b/store/store.go @@ -36,6 +36,7 @@ type Store interface { Session() SessionStore OAuth() OAuthStore System() SystemStore + Webhook() WebhookStore Close() } @@ -137,3 +138,10 @@ type SystemStore interface { Update(system *model.System) StoreChannel Get() StoreChannel } + +type WebhookStore interface { + SaveIncoming(webhook *model.IncomingWebhook) StoreChannel + GetIncoming(id string) StoreChannel + GetIncomingByUser(userId string) StoreChannel + DeleteIncoming(webhookId string, time int64) StoreChannel +} -- cgit v1.2.3-1-g7c22