From 69cac604e09c139845d2f63ac95fb702fb5a9fe1 Mon Sep 17 00:00:00 2001 From: Joram Wilander Date: Tue, 21 Feb 2017 19:42:34 -0500 Subject: Implement create and get incoming webhook endpoints for APIv4 (#5407) * Implement POST /hooks/incoming endpoint for APIv4 * Implement GET /hooks/incoming endpoint for APIv4 * Updates per feedback --- api/webhook.go | 51 ++++---------- api4/api.go | 11 +-- api4/apitestlib.go | 15 ++++ api4/webhook.go | 85 +++++++++++++++++++++++ api4/webhook_test.go | 150 ++++++++++++++++++++++++++++++++++++++++ app/webhook.go | 39 +++++++++++ model/client4.go | 38 ++++++++++ store/sql_channel_store.go | 2 +- store/sql_webhook_store.go | 29 +++++++- store/sql_webhook_store_test.go | 38 +++++++++- store/store.go | 3 +- 11 files changed, 412 insertions(+), 49 deletions(-) create mode 100644 api4/webhook.go create mode 100644 api4/webhook_test.go diff --git a/api/webhook.go b/api/webhook.go index 248df6726..638607a32 100644 --- a/api/webhook.go +++ b/api/webhook.go @@ -37,37 +37,23 @@ func InitWebhook() { } func createIncomingHook(c *Context, w http.ResponseWriter, r *http.Request) { - if !utils.Cfg.ServiceSettings.EnableIncomingWebhooks { - c.Err = model.NewLocAppError("createIncomingHook", "api.webhook.create_incoming.disabled.app_errror", nil, "") - c.Err.StatusCode = http.StatusNotImplemented - return - } - - if !app.SessionHasPermissionToTeam(c.Session, c.TeamId, model.PERMISSION_MANAGE_WEBHOOKS) { - c.SetPermissionError(model.PERMISSION_MANAGE_WEBHOOKS) - return - } - - c.LogAudit("attempt") - hook := model.IncomingWebhookFromJson(r.Body) - if hook == nil { c.SetInvalidParam("createIncomingHook", "webhook") return } - cchan := app.Srv.Store.Channel().Get(hook.ChannelId, true) + channel, err := app.GetChannel(hook.ChannelId) + if err != nil { + c.Err = err + return + } - hook.UserId = c.Session.UserId - hook.TeamId = c.TeamId + c.LogAudit("attempt") - var channel *model.Channel - if result := <-cchan; result.Err != nil { - c.Err = result.Err + if !app.SessionHasPermissionToTeam(c.Session, channel.TeamId, model.PERMISSION_MANAGE_WEBHOOKS) { + c.SetPermissionError(model.PERMISSION_MANAGE_WEBHOOKS) return - } else { - channel = result.Data.(*model.Channel) } if channel.Type != model.CHANNEL_OPEN && !app.SessionHasPermissionToChannel(c.Session, channel.Id, model.PERMISSION_READ_CHANNEL) { @@ -76,13 +62,12 @@ func createIncomingHook(c *Context, w http.ResponseWriter, r *http.Request) { return } - if result := <-app.Srv.Store.Webhook().SaveIncoming(hook); result.Err != nil { - c.Err = result.Err + if incomingHook, err := app.CreateIncomingWebhookForChannel(c.Session.UserId, channel, hook); err != nil { + c.Err = err return } else { c.LogAudit("success") - rhook := result.Data.(*model.IncomingWebhook) - w.Write([]byte(rhook.ToJson())) + w.Write([]byte(incomingHook.ToJson())) } } @@ -132,23 +117,15 @@ func deleteIncomingHook(c *Context, w http.ResponseWriter, r *http.Request) { } func getIncomingHooks(c *Context, w http.ResponseWriter, r *http.Request) { - if !utils.Cfg.ServiceSettings.EnableIncomingWebhooks { - c.Err = model.NewLocAppError("getIncomingHooks", "api.webhook.get_incoming.disabled.app_error", nil, "") - c.Err.StatusCode = http.StatusNotImplemented - return - } - if !app.SessionHasPermissionToTeam(c.Session, c.TeamId, model.PERMISSION_MANAGE_WEBHOOKS) { - c.Err = model.NewLocAppError("getIncomingHooks", "api.command.admin_only.app_error", nil, "") - c.Err.StatusCode = http.StatusForbidden + c.SetPermissionError(model.PERMISSION_MANAGE_WEBHOOKS) return } - if result := <-app.Srv.Store.Webhook().GetIncomingByTeam(c.TeamId); result.Err != nil { - c.Err = result.Err + if hooks, err := app.GetIncomingWebhooksForTeamPage(c.TeamId, 0, 100); err != nil { + c.Err = err return } else { - hooks := result.Data.([]*model.IncomingWebhook) w.Write([]byte(model.IncomingWebhookListToJson(hooks))) } } diff --git a/api4/api.go b/api4/api.go index df45ff1a3..ca43e7275 100644 --- a/api4/api.go +++ b/api4/api.go @@ -52,11 +52,11 @@ type Routes struct { Command *mux.Router // 'api/v4/commands/{command_id:[A-Za-z0-9]+}' CommandsForTeam *mux.Router // 'api/v4/teams/{team_id:[A-Za-z0-9]+}/commands' - Hooks *mux.Router // 'api/v4/teams/hooks' - IncomingHooks *mux.Router // 'api/v4/teams/hooks/incoming' - IncomingHook *mux.Router // 'api/v4/teams/hooks/incoming/{hook_id:[A-Za-z0-9]+}' - OutgoingHooks *mux.Router // 'api/v4/teams/hooks/outgoing' - OutgoingHook *mux.Router // 'api/v4/teams/hooks/outgoing/{hook_id:[A-Za-z0-9]+}' + Hooks *mux.Router // 'api/v4/hooks' + IncomingHooks *mux.Router // 'api/v4/hooks/incoming' + IncomingHook *mux.Router // 'api/v4/hooks/incoming/{hook_id:[A-Za-z0-9]+}' + OutgoingHooks *mux.Router // 'api/v4/hooks/outgoing' + OutgoingHook *mux.Router // 'api/v4/hooks/outgoing/{hook_id:[A-Za-z0-9]+}' OAuth *mux.Router // 'api/v4/oauth' @@ -145,6 +145,7 @@ func InitApi(full bool) { InitPost() InitFile() InitSystem() + InitWebhook() app.Srv.Router.Handle("/api/v4/{anything:.*}", http.HandlerFunc(Handle404)) diff --git a/api4/apitestlib.go b/api4/apitestlib.go index bb5ee1594..d77438e04 100644 --- a/api4/apitestlib.go +++ b/api4/apitestlib.go @@ -422,6 +422,21 @@ func CheckBadRequestStatus(t *testing.T, resp *model.Response) { } } +func CheckNotImplementedStatus(t *testing.T, resp *model.Response) { + if resp.Error == nil { + debug.PrintStack() + t.Fatal("should have errored with status:" + strconv.Itoa(http.StatusNotImplemented)) + return + } + + if resp.StatusCode != http.StatusNotImplemented { + debug.PrintStack() + t.Log("actual: " + strconv.Itoa(resp.StatusCode)) + t.Log("expected: " + strconv.Itoa(http.StatusNotImplemented)) + t.Fatal("wrong status code") + } +} + func CheckErrorMessage(t *testing.T, resp *model.Response, errorId string) { if resp.Error == nil { debug.PrintStack() diff --git a/api4/webhook.go b/api4/webhook.go new file mode 100644 index 000000000..9efab6ae2 --- /dev/null +++ b/api4/webhook.go @@ -0,0 +1,85 @@ +// Copyright (c) 2017 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package api4 + +import ( + "net/http" + + l4g "github.com/alecthomas/log4go" + "github.com/mattermost/platform/app" + "github.com/mattermost/platform/model" + "github.com/mattermost/platform/utils" +) + +func InitWebhook() { + l4g.Debug(utils.T("api.webhook.init.debug")) + + BaseRoutes.IncomingHooks.Handle("", ApiSessionRequired(createIncomingHook)).Methods("POST") + BaseRoutes.IncomingHooks.Handle("", ApiSessionRequired(getIncomingHooks)).Methods("GET") +} + +func createIncomingHook(c *Context, w http.ResponseWriter, r *http.Request) { + hook := model.IncomingWebhookFromJson(r.Body) + if hook == nil { + c.SetInvalidParam("webhook") + return + } + + channel, err := app.GetChannel(hook.ChannelId) + if err != nil { + c.Err = err + return + } + + c.LogAudit("attempt") + + if !app.SessionHasPermissionToTeam(c.Session, channel.TeamId, model.PERMISSION_MANAGE_WEBHOOKS) { + c.SetPermissionError(model.PERMISSION_MANAGE_WEBHOOKS) + return + } + + if channel.Type != model.CHANNEL_OPEN && !app.SessionHasPermissionToChannel(c.Session, channel.Id, model.PERMISSION_READ_CHANNEL) { + c.LogAudit("fail - bad channel permissions") + c.SetPermissionError(model.PERMISSION_READ_CHANNEL) + return + } + + if incomingHook, err := app.CreateIncomingWebhookForChannel(c.Session.UserId, channel, hook); err != nil { + c.Err = err + return + } else { + c.LogAudit("success") + w.Write([]byte(incomingHook.ToJson())) + } +} + +func getIncomingHooks(c *Context, w http.ResponseWriter, r *http.Request) { + teamId := r.URL.Query().Get("team_id") + + var hooks []*model.IncomingWebhook + var err *model.AppError + + if len(teamId) > 0 { + if !app.SessionHasPermissionToTeam(c.Session, teamId, model.PERMISSION_MANAGE_WEBHOOKS) { + c.SetPermissionError(model.PERMISSION_MANAGE_WEBHOOKS) + return + } + + hooks, err = app.GetIncomingWebhooksForTeamPage(teamId, c.Params.Page, c.Params.PerPage) + } else { + if !app.SessionHasPermissionTo(c.Session, model.PERMISSION_MANAGE_WEBHOOKS) { + c.SetPermissionError(model.PERMISSION_MANAGE_WEBHOOKS) + return + } + + hooks, err = app.GetIncomingWebhooksPage(c.Params.Page, c.Params.PerPage) + } + + if err != nil { + c.Err = err + return + } + + w.Write([]byte(model.IncomingWebhookListToJson(hooks))) +} diff --git a/api4/webhook_test.go b/api4/webhook_test.go new file mode 100644 index 000000000..a6705f6e1 --- /dev/null +++ b/api4/webhook_test.go @@ -0,0 +1,150 @@ +// Copyright (c) 2017 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package api4 + +import ( + "testing" + + "github.com/mattermost/platform/model" + "github.com/mattermost/platform/utils" +) + +func TestCreateIncomingWebhook(t *testing.T) { + th := Setup().InitBasic().InitSystemAdmin() + defer TearDown() + Client := th.Client + + enableIncomingHooks := utils.Cfg.ServiceSettings.EnableIncomingWebhooks + enableAdminOnlyHooks := utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations + defer func() { + utils.Cfg.ServiceSettings.EnableIncomingWebhooks = enableIncomingHooks + utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations = enableAdminOnlyHooks + utils.SetDefaultRolesBasedOnConfig() + }() + utils.Cfg.ServiceSettings.EnableIncomingWebhooks = true + *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations = true + utils.SetDefaultRolesBasedOnConfig() + + hook := &model.IncomingWebhook{ChannelId: th.BasicChannel.Id} + + rhook, resp := th.SystemAdminClient.CreateIncomingWebhook(hook) + CheckNoError(t, resp) + + if rhook.ChannelId != hook.ChannelId { + t.Fatal("channel ids didn't match") + } + + if rhook.UserId != th.SystemAdminUser.Id { + t.Fatal("user ids didn't match") + } + + if rhook.TeamId != th.BasicTeam.Id { + t.Fatal("team ids didn't match") + } + + hook.ChannelId = "junk" + _, resp = th.SystemAdminClient.CreateIncomingWebhook(hook) + CheckNotFoundStatus(t, resp) + + hook.ChannelId = th.BasicChannel.Id + th.LoginTeamAdmin() + _, resp = Client.CreateIncomingWebhook(hook) + CheckNoError(t, resp) + + th.LoginBasic() + _, resp = Client.CreateIncomingWebhook(hook) + CheckForbiddenStatus(t, resp) + + *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations = false + utils.SetDefaultRolesBasedOnConfig() + + _, resp = Client.CreateIncomingWebhook(hook) + CheckNoError(t, resp) + + utils.Cfg.ServiceSettings.EnableIncomingWebhooks = false + _, resp = Client.CreateIncomingWebhook(hook) + CheckNotImplementedStatus(t, resp) +} + +func TestGetIncomingWebhooks(t *testing.T) { + th := Setup().InitBasic().InitSystemAdmin() + defer TearDown() + Client := th.Client + + enableIncomingHooks := utils.Cfg.ServiceSettings.EnableIncomingWebhooks + enableAdminOnlyHooks := utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations + defer func() { + utils.Cfg.ServiceSettings.EnableIncomingWebhooks = enableIncomingHooks + utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations = enableAdminOnlyHooks + utils.SetDefaultRolesBasedOnConfig() + }() + utils.Cfg.ServiceSettings.EnableIncomingWebhooks = true + *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations = true + utils.SetDefaultRolesBasedOnConfig() + + hook := &model.IncomingWebhook{ChannelId: th.BasicChannel.Id} + rhook, resp := th.SystemAdminClient.CreateIncomingWebhook(hook) + CheckNoError(t, resp) + + hooks, resp := th.SystemAdminClient.GetIncomingWebhooks(0, 1000, "") + CheckNoError(t, resp) + + found := false + for _, h := range hooks { + if rhook.Id == h.Id { + found = true + } + } + + if !found { + t.Fatal("missing hook") + } + + hooks, resp = th.SystemAdminClient.GetIncomingWebhooks(0, 1, "") + CheckNoError(t, resp) + + if len(hooks) != 1 { + t.Fatal("should only be 1") + } + + hooks, resp = th.SystemAdminClient.GetIncomingWebhooksForTeam(th.BasicTeam.Id, 0, 1000, "") + CheckNoError(t, resp) + + found = false + for _, h := range hooks { + if rhook.Id == h.Id { + found = true + } + } + + if !found { + t.Fatal("missing hook") + } + + hooks, resp = th.SystemAdminClient.GetIncomingWebhooksForTeam(model.NewId(), 0, 1000, "") + CheckNoError(t, resp) + + if len(hooks) != 0 { + t.Fatal("no hooks should be returned") + } + + _, resp = Client.GetIncomingWebhooks(0, 1000, "") + CheckForbiddenStatus(t, resp) + + *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations = false + utils.SetDefaultRolesBasedOnConfig() + + _, resp = Client.GetIncomingWebhooksForTeam(th.BasicTeam.Id, 0, 1000, "") + CheckNoError(t, resp) + + _, resp = Client.GetIncomingWebhooksForTeam(model.NewId(), 0, 1000, "") + CheckForbiddenStatus(t, resp) + + _, resp = Client.GetIncomingWebhooks(0, 1000, "") + CheckForbiddenStatus(t, resp) + + Client.Logout() + _, resp = Client.GetIncomingWebhooks(0, 1000, "") + CheckUnauthorizedStatus(t, resp) +} diff --git a/app/webhook.go b/app/webhook.go index 89e322180..ff7f2726e 100644 --- a/app/webhook.go +++ b/app/webhook.go @@ -192,3 +192,42 @@ func CreateWebhookPost(userId, teamId, channelId, text, overrideUsername, overri return post, nil } + +func CreateIncomingWebhookForChannel(userId string, channel *model.Channel, hook *model.IncomingWebhook) (*model.IncomingWebhook, *model.AppError) { + if !utils.Cfg.ServiceSettings.EnableIncomingWebhooks { + return nil, model.NewAppError("CreateIncomingWebhookForChannel", "api.webhook.create_incoming.disabled.app_errror", nil, "", http.StatusNotImplemented) + } + + hook.UserId = userId + hook.TeamId = channel.TeamId + + if result := <-Srv.Store.Webhook().SaveIncoming(hook); result.Err != nil { + return nil, result.Err + } else { + return result.Data.(*model.IncomingWebhook), nil + } +} + +func GetIncomingWebhooksForTeamPage(teamId string, page, perPage int) ([]*model.IncomingWebhook, *model.AppError) { + if !utils.Cfg.ServiceSettings.EnableIncomingWebhooks { + return nil, model.NewAppError("GetIncomingWebhooksForTeamPage", "api.webhook.get_incoming.disabled.app_error", nil, "", http.StatusNotImplemented) + } + + if result := <-Srv.Store.Webhook().GetIncomingByTeam(teamId, page*perPage, perPage); result.Err != nil { + return nil, result.Err + } else { + return result.Data.([]*model.IncomingWebhook), nil + } +} + +func GetIncomingWebhooksPage(page, perPage int) ([]*model.IncomingWebhook, *model.AppError) { + if !utils.Cfg.ServiceSettings.EnableIncomingWebhooks { + return nil, model.NewAppError("GetIncomingWebhooksPage", "api.webhook.get_incoming.disabled.app_error", nil, "", http.StatusNotImplemented) + } + + if result := <-Srv.Store.Webhook().GetIncomingList(page*perPage, perPage); result.Err != nil { + return nil, result.Err + } else { + return result.Data.([]*model.IncomingWebhook), nil + } +} diff --git a/model/client4.go b/model/client4.go index 6ace37dca..fb0601a38 100644 --- a/model/client4.go +++ b/model/client4.go @@ -133,6 +133,10 @@ func (c *Client4) GetSystemRoute() string { return fmt.Sprintf("/system") } +func (c *Client4) GetIncomingWebhooksRoute() string { + return fmt.Sprintf("/hooks/incoming") +} + func (c *Client4) DoApiGet(url string, etag string) (*http.Response, *AppError) { return c.DoApiRequest(http.MethodGet, url, "", etag) } @@ -775,3 +779,37 @@ func (c *Client4) GetPing() (bool, *Response) { return CheckStatusOK(r), BuildResponse(r) } } + +// Webhooks Section + +// CreateIncomingWebhook creates an incoming webhook for a channel. +func (c *Client4) CreateIncomingWebhook(hook *IncomingWebhook) (*IncomingWebhook, *Response) { + if r, err := c.DoApiPost(c.GetIncomingWebhooksRoute(), hook.ToJson()); err != nil { + return nil, &Response{StatusCode: r.StatusCode, Error: err} + } else { + defer closeBody(r) + return IncomingWebhookFromJson(r.Body), BuildResponse(r) + } +} + +// GetIncomingWebhooks returns a page of incoming webhooks on the system. Page counting starts at 0. +func (c *Client4) GetIncomingWebhooks(page int, perPage int, etag string) ([]*IncomingWebhook, *Response) { + query := fmt.Sprintf("?page=%v&per_page=%v", page, perPage) + if r, err := c.DoApiGet(c.GetIncomingWebhooksRoute()+query, etag); err != nil { + return nil, &Response{StatusCode: r.StatusCode, Error: err} + } else { + defer closeBody(r) + return IncomingWebhookListFromJson(r.Body), BuildResponse(r) + } +} + +// GetIncomingWebhooksForTeam returns a page of incoming webhooks for a team. Page counting starts at 0. +func (c *Client4) GetIncomingWebhooksForTeam(teamId string, page int, perPage int, etag string) ([]*IncomingWebhook, *Response) { + query := fmt.Sprintf("?page=%v&per_page=%v&team_id=%v", page, perPage, teamId) + if r, err := c.DoApiGet(c.GetIncomingWebhooksRoute()+query, etag); err != nil { + return nil, &Response{StatusCode: r.StatusCode, Error: err} + } else { + defer closeBody(r) + return IncomingWebhookListFromJson(r.Body), BuildResponse(r) + } +} diff --git a/store/sql_channel_store.go b/store/sql_channel_store.go index 503b646f6..03bc70c75 100644 --- a/store/sql_channel_store.go +++ b/store/sql_channel_store.go @@ -357,7 +357,7 @@ func (s SqlChannelStore) get(id string, master bool, allowFromCache bool) StoreC if obj, err := db.Get(model.Channel{}, id); err != nil { result.Err = model.NewLocAppError("SqlChannelStore.Get", "store.sql_channel.get.find.app_error", nil, "id="+id+", "+err.Error()) } else if obj == nil { - result.Err = model.NewLocAppError("SqlChannelStore.Get", "store.sql_channel.get.existing.app_error", nil, "id="+id) + result.Err = model.NewAppError("SqlChannelStore.Get", "store.sql_channel.get.existing.app_error", nil, "id="+id, http.StatusBadRequest) } else { result.Data = obj.(*model.Channel) channelCache.AddWithExpiresInSecs(id, obj.(*model.Channel), CHANNEL_CACHE_SEC) diff --git a/store/sql_webhook_store.go b/store/sql_webhook_store.go index 4022aff7f..0e61130ad 100644 --- a/store/sql_webhook_store.go +++ b/store/sql_webhook_store.go @@ -4,6 +4,8 @@ package store import ( + "net/http" + "github.com/mattermost/platform/einterfaces" "github.com/mattermost/platform/model" "github.com/mattermost/platform/utils" @@ -183,7 +185,28 @@ func (s SqlWebhookStore) PermanentDeleteIncomingByUser(userId string) StoreChann return storeChannel } -func (s SqlWebhookStore) GetIncomingByTeam(teamId string) StoreChannel { +func (s SqlWebhookStore) GetIncomingList(offset, limit int) StoreChannel { + storeChannel := make(StoreChannel, 1) + + go func() { + result := StoreResult{} + + var webhooks []*model.IncomingWebhook + + if _, err := s.GetReplica().Select(&webhooks, "SELECT * FROM IncomingWebhooks WHERE DeleteAt = 0 LIMIT :Limit OFFSET :Offset", map[string]interface{}{"Limit": limit, "Offset": offset}); err != nil { + result.Err = model.NewAppError("SqlWebhookStore.GetIncomingList", "store.sql_webhooks.get_incoming_by_user.app_error", nil, "err="+err.Error(), http.StatusInternalServerError) + } + + result.Data = webhooks + + storeChannel <- result + close(storeChannel) + }() + + return storeChannel +} + +func (s SqlWebhookStore) GetIncomingByTeam(teamId string, offset, limit int) StoreChannel { storeChannel := make(StoreChannel, 1) go func() { @@ -191,8 +214,8 @@ func (s SqlWebhookStore) GetIncomingByTeam(teamId string) StoreChannel { var webhooks []*model.IncomingWebhook - if _, err := s.GetReplica().Select(&webhooks, "SELECT * FROM IncomingWebhooks WHERE TeamId = :TeamId AND DeleteAt = 0", map[string]interface{}{"TeamId": teamId}); err != nil { - result.Err = model.NewLocAppError("SqlWebhookStore.GetIncomingByUser", "store.sql_webhooks.get_incoming_by_user.app_error", nil, "teamId="+teamId+", err="+err.Error()) + if _, err := s.GetReplica().Select(&webhooks, "SELECT * FROM IncomingWebhooks WHERE TeamId = :TeamId AND DeleteAt = 0 LIMIT :Limit OFFSET :Offset", map[string]interface{}{"TeamId": teamId, "Limit": limit, "Offset": offset}); err != nil { + result.Err = model.NewAppError("SqlWebhookStore.GetIncomingByUser", "store.sql_webhooks.get_incoming_by_user.app_error", nil, "teamId="+teamId+", err="+err.Error(), http.StatusInternalServerError) } result.Data = webhooks diff --git a/store/sql_webhook_store_test.go b/store/sql_webhook_store_test.go index 401b0f904..3d79d9ad3 100644 --- a/store/sql_webhook_store_test.go +++ b/store/sql_webhook_store_test.go @@ -60,6 +60,40 @@ func TestWebhookStoreGetIncoming(t *testing.T) { } } +func TestWebhookStoreGetIncomingList(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().GetIncomingList(0, 1000); r1.Err != nil { + t.Fatal(r1.Err) + } else { + found := false + hooks := r1.Data.([]*model.IncomingWebhook) + for _, hook := range hooks { + if hook.Id == o1.Id { + found = true + } + } + if !found { + t.Fatal("missing webhook") + } + } + + if result := <-store.Webhook().GetIncomingList(0, 1); result.Err != nil { + t.Fatal(result.Err) + } else { + if len(result.Data.([]*model.IncomingWebhook)) != 1 { + t.Fatal("only 1 should be returned") + } + } +} + func TestWebhookStoreGetIncomingByTeam(t *testing.T) { Setup() @@ -70,7 +104,7 @@ func TestWebhookStoreGetIncomingByTeam(t *testing.T) { o1 = (<-store.Webhook().SaveIncoming(o1)).Data.(*model.IncomingWebhook) - if r1 := <-store.Webhook().GetIncomingByTeam(o1.TeamId); r1.Err != nil { + if r1 := <-store.Webhook().GetIncomingByTeam(o1.TeamId, 0, 100); r1.Err != nil { t.Fatal(r1.Err) } else { if r1.Data.([]*model.IncomingWebhook)[0].CreateAt != o1.CreateAt { @@ -78,7 +112,7 @@ func TestWebhookStoreGetIncomingByTeam(t *testing.T) { } } - if result := <-store.Webhook().GetIncomingByTeam("123"); result.Err != nil { + if result := <-store.Webhook().GetIncomingByTeam("123", 0, 100); result.Err != nil { t.Fatal(result.Err) } else { if len(result.Data.([]*model.IncomingWebhook)) != 0 { diff --git a/store/store.go b/store/store.go index 6c78a235d..d0c403460 100644 --- a/store/store.go +++ b/store/store.go @@ -252,7 +252,8 @@ type SystemStore interface { type WebhookStore interface { SaveIncoming(webhook *model.IncomingWebhook) StoreChannel GetIncoming(id string, allowFromCache bool) StoreChannel - GetIncomingByTeam(teamId string) StoreChannel + GetIncomingList(offset, limit int) StoreChannel + GetIncomingByTeam(teamId string, offset, limit int) StoreChannel GetIncomingByChannel(channelId string) StoreChannel DeleteIncoming(webhookId string, time int64) StoreChannel PermanentDeleteIncomingByUser(userId string) StoreChannel -- cgit v1.2.3-1-g7c22