From 19c67d7fe35f92ae8a288dcdb9877d3bede41a61 Mon Sep 17 00:00:00 2001 From: Joram Wilander Date: Mon, 13 Mar 2017 10:40:43 -0400 Subject: Implement GET and POST /hooks/outgoing endpoints for APIv4 (#5645) --- api4/webhook.go | 72 +++++++++++++++++- api4/webhook_test.go | 162 ++++++++++++++++++++++++++++++++++++++++ app/channel.go | 2 +- app/webhook.go | 32 +++++++- model/client4.go | 47 ++++++++++++ store/sql_channel_store.go | 4 +- store/sql_webhook_store.go | 43 ++++++++++- store/sql_webhook_store_test.go | 61 ++++++++++++++- store/store.go | 5 +- 9 files changed, 409 insertions(+), 19 deletions(-) diff --git a/api4/webhook.go b/api4/webhook.go index 19a851390..923f66ad3 100644 --- a/api4/webhook.go +++ b/api4/webhook.go @@ -17,15 +17,17 @@ func InitWebhook() { BaseRoutes.IncomingHooks.Handle("", ApiSessionRequired(createIncomingHook)).Methods("POST") BaseRoutes.IncomingHooks.Handle("", ApiSessionRequired(getIncomingHooks)).Methods("GET") - BaseRoutes.IncomingHook.Handle("", ApiSessionRequired(getIncomingHook)).Methods("GET") BaseRoutes.IncomingHook.Handle("", ApiSessionRequired(deleteIncomingHook)).Methods("DELETE") + + BaseRoutes.OutgoingHooks.Handle("", ApiSessionRequired(createOutgoingHook)).Methods("POST") + BaseRoutes.OutgoingHooks.Handle("", ApiSessionRequired(getOutgoingHooks)).Methods("GET") } func createIncomingHook(c *Context, w http.ResponseWriter, r *http.Request) { hook := model.IncomingWebhookFromJson(r.Body) if hook == nil { - c.SetInvalidParam("webhook") + c.SetInvalidParam("incoming_webhook") return } @@ -53,6 +55,7 @@ func createIncomingHook(c *Context, w http.ResponseWriter, r *http.Request) { return } else { c.LogAudit("success") + w.WriteHeader(http.StatusCreated) w.Write([]byte(incomingHook.ToJson())) } } @@ -158,3 +161,68 @@ func deleteIncomingHook(c *Context, w http.ResponseWriter, r *http.Request) { } } } + +func createOutgoingHook(c *Context, w http.ResponseWriter, r *http.Request) { + hook := model.OutgoingWebhookFromJson(r.Body) + if hook == nil { + c.SetInvalidParam("outgoing_webhook") + return + } + + c.LogAudit("attempt") + + hook.CreatorId = c.Session.UserId + + if !app.SessionHasPermissionToTeam(c.Session, hook.TeamId, model.PERMISSION_MANAGE_WEBHOOKS) { + c.SetPermissionError(model.PERMISSION_MANAGE_WEBHOOKS) + return + } + + if rhook, err := app.CreateOutgoingWebhook(hook); err != nil { + c.LogAudit("fail") + c.Err = err + return + } else { + c.LogAudit("success") + w.WriteHeader(http.StatusCreated) + w.Write([]byte(rhook.ToJson())) + } +} + +func getOutgoingHooks(c *Context, w http.ResponseWriter, r *http.Request) { + channelId := r.URL.Query().Get("channel_id") + teamId := r.URL.Query().Get("team_id") + + var hooks []*model.OutgoingWebhook + var err *model.AppError + + if len(channelId) > 0 { + if !app.SessionHasPermissionToChannel(c.Session, channelId, model.PERMISSION_MANAGE_WEBHOOKS) { + c.SetPermissionError(model.PERMISSION_MANAGE_WEBHOOKS) + return + } + + hooks, err = app.GetOutgoingWebhooksForChannelPage(channelId, c.Params.Page, c.Params.PerPage) + } else if len(teamId) > 0 { + if !app.SessionHasPermissionToTeam(c.Session, teamId, model.PERMISSION_MANAGE_WEBHOOKS) { + c.SetPermissionError(model.PERMISSION_MANAGE_WEBHOOKS) + return + } + + hooks, err = app.GetOutgoingWebhooksForTeamPage(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.GetOutgoingWebhooksPage(c.Params.Page, c.Params.PerPage) + } + + if err != nil { + c.Err = err + return + } + + w.Write([]byte(model.OutgoingWebhookListToJson(hooks))) +} diff --git a/api4/webhook_test.go b/api4/webhook_test.go index bfd75c7ec..2d8a1e8f9 100644 --- a/api4/webhook_test.go +++ b/api4/webhook_test.go @@ -256,3 +256,165 @@ func TestDeleteIncomingWebhook(t *testing.T) { CheckForbiddenStatus(t, resp) }) } + +func TestCreateOutgoingWebhook(t *testing.T) { + th := Setup().InitBasic().InitSystemAdmin() + defer TearDown() + Client := th.Client + + enableOutgoingHooks := utils.Cfg.ServiceSettings.EnableOutgoingWebhooks + enableAdminOnlyHooks := utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations + defer func() { + utils.Cfg.ServiceSettings.EnableOutgoingWebhooks = enableOutgoingHooks + utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations = enableAdminOnlyHooks + utils.SetDefaultRolesBasedOnConfig() + }() + utils.Cfg.ServiceSettings.EnableOutgoingWebhooks = true + *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations = true + utils.SetDefaultRolesBasedOnConfig() + + hook := &model.OutgoingWebhook{ChannelId: th.BasicChannel.Id, TeamId: th.BasicChannel.TeamId, CallbackURLs: []string{"http://nowhere.com"}} + + rhook, resp := th.SystemAdminClient.CreateOutgoingWebhook(hook) + CheckNoError(t, resp) + + if rhook.ChannelId != hook.ChannelId { + t.Fatal("channel ids didn't match") + } else if rhook.CreatorId != th.SystemAdminUser.Id { + t.Fatal("user ids didn't match") + } else if rhook.TeamId != th.BasicChannel.TeamId { + t.Fatal("team ids didn't match") + } + + hook.ChannelId = "junk" + _, resp = th.SystemAdminClient.CreateOutgoingWebhook(hook) + CheckNotFoundStatus(t, resp) + + hook.ChannelId = th.BasicChannel.Id + th.LoginTeamAdmin() + _, resp = Client.CreateOutgoingWebhook(hook) + CheckNoError(t, resp) + + th.LoginBasic() + _, resp = Client.CreateOutgoingWebhook(hook) + CheckForbiddenStatus(t, resp) + + *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations = false + utils.SetDefaultRolesBasedOnConfig() + + _, resp = Client.CreateOutgoingWebhook(hook) + CheckNoError(t, resp) + + utils.Cfg.ServiceSettings.EnableOutgoingWebhooks = false + _, resp = Client.CreateOutgoingWebhook(hook) + CheckNotImplementedStatus(t, resp) +} + +func TestGetOutgoingWebhooks(t *testing.T) { + th := Setup().InitBasic().InitSystemAdmin() + defer TearDown() + Client := th.Client + + enableOutgoingHooks := utils.Cfg.ServiceSettings.EnableOutgoingWebhooks + enableAdminOnlyHooks := utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations + defer func() { + utils.Cfg.ServiceSettings.EnableOutgoingWebhooks = enableOutgoingHooks + utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations = enableAdminOnlyHooks + utils.SetDefaultRolesBasedOnConfig() + }() + utils.Cfg.ServiceSettings.EnableOutgoingWebhooks = true + *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations = true + utils.SetDefaultRolesBasedOnConfig() + + hook := &model.OutgoingWebhook{ChannelId: th.BasicChannel.Id, TeamId: th.BasicChannel.TeamId, CallbackURLs: []string{"http://nowhere.com"}} + rhook, resp := th.SystemAdminClient.CreateOutgoingWebhook(hook) + CheckNoError(t, resp) + + hooks, resp := th.SystemAdminClient.GetOutgoingWebhooks(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.GetOutgoingWebhooks(0, 1, "") + CheckNoError(t, resp) + + if len(hooks) != 1 { + t.Fatal("should only be 1") + } + + hooks, resp = th.SystemAdminClient.GetOutgoingWebhooksForTeam(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.GetOutgoingWebhooksForTeam(model.NewId(), 0, 1000, "") + CheckNoError(t, resp) + + if len(hooks) != 0 { + t.Fatal("no hooks should be returned") + } + + hooks, resp = th.SystemAdminClient.GetOutgoingWebhooksForChannel(th.BasicChannel.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.GetOutgoingWebhooksForChannel(model.NewId(), 0, 1000, "") + CheckNoError(t, resp) + + if len(hooks) != 0 { + t.Fatal("no hooks should be returned") + } + + _, resp = Client.GetOutgoingWebhooks(0, 1000, "") + CheckForbiddenStatus(t, resp) + + *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations = false + utils.SetDefaultRolesBasedOnConfig() + + _, resp = Client.GetOutgoingWebhooksForTeam(th.BasicTeam.Id, 0, 1000, "") + CheckNoError(t, resp) + + _, resp = Client.GetOutgoingWebhooksForTeam(model.NewId(), 0, 1000, "") + CheckForbiddenStatus(t, resp) + + _, resp = Client.GetOutgoingWebhooksForChannel(th.BasicChannel.Id, 0, 1000, "") + CheckNoError(t, resp) + + _, resp = Client.GetOutgoingWebhooksForChannel(model.NewId(), 0, 1000, "") + CheckForbiddenStatus(t, resp) + + _, resp = Client.GetOutgoingWebhooks(0, 1000, "") + CheckForbiddenStatus(t, resp) + + Client.Logout() + _, resp = Client.GetOutgoingWebhooks(0, 1000, "") + CheckUnauthorizedStatus(t, resp) +} diff --git a/app/channel.go b/app/channel.go index 67caed94d..59b71c0a5 100644 --- a/app/channel.go +++ b/app/channel.go @@ -313,7 +313,7 @@ func UpdateChannelMemberNotifyProps(data map[string]string, channelId string, us func DeleteChannel(channel *model.Channel, userId string) *model.AppError { uc := Srv.Store.User().Get(userId) ihc := Srv.Store.Webhook().GetIncomingByChannel(channel.Id) - ohc := Srv.Store.Webhook().GetOutgoingByChannel(channel.Id) + ohc := Srv.Store.Webhook().GetOutgoingByChannel(channel.Id, -1, -1) if uresult := <-uc; uresult.Err != nil { return uresult.Err diff --git a/app/webhook.go b/app/webhook.go index 6f1cec4a8..555b0f51d 100644 --- a/app/webhook.go +++ b/app/webhook.go @@ -33,7 +33,7 @@ func handleWebhookEvents(post *model.Post, team *model.Team, channel *model.Chan return nil } - hchan := Srv.Store.Webhook().GetOutgoingByTeam(team.Id) + hchan := Srv.Store.Webhook().GetOutgoingByTeam(team.Id, -1, -1) result := <-hchan if result.Err != nil { return result.Err @@ -304,7 +304,7 @@ func CreateOutgoingWebhook(hook *model.OutgoingWebhook) (*model.OutgoingWebhook, return nil, model.NewAppError("CreateOutgoingWebhook", "api.webhook.create_outgoing.triggers.app_error", nil, "", http.StatusBadRequest) } - if result := <-Srv.Store.Webhook().GetOutgoingByTeam(hook.TeamId); result.Err != nil { + if result := <-Srv.Store.Webhook().GetOutgoingByTeam(hook.TeamId, -1, -1); result.Err != nil { return nil, result.Err } else { allHooks := result.Data.([]*model.OutgoingWebhook) @@ -349,7 +349,7 @@ func UpdateOutgoingWebhook(oldHook, updatedHook *model.OutgoingWebhook) (*model. } var result store.StoreResult - if result = <-Srv.Store.Webhook().GetOutgoingByTeam(oldHook.TeamId); result.Err != nil { + if result = <-Srv.Store.Webhook().GetOutgoingByTeam(oldHook.TeamId, -1, -1); result.Err != nil { return nil, result.Err } @@ -389,12 +389,36 @@ func GetOutgoingWebhook(hookId string) (*model.OutgoingWebhook, *model.AppError) } } +func GetOutgoingWebhooksPage(page, perPage int) ([]*model.OutgoingWebhook, *model.AppError) { + if !utils.Cfg.ServiceSettings.EnableOutgoingWebhooks { + return nil, model.NewAppError("GetOutgoingWebhooksPage", "api.outgoing_webhook.disabled.app_error", nil, "", http.StatusNotImplemented) + } + + if result := <-Srv.Store.Webhook().GetOutgoingList(page*perPage, perPage); result.Err != nil { + return nil, result.Err + } else { + return result.Data.([]*model.OutgoingWebhook), nil + } +} + +func GetOutgoingWebhooksForChannelPage(channelId string, page, perPage int) ([]*model.OutgoingWebhook, *model.AppError) { + if !utils.Cfg.ServiceSettings.EnableOutgoingWebhooks { + return nil, model.NewAppError("GetOutgoingWebhooksForChannelPage", "api.outgoing_webhook.disabled.app_error", nil, "", http.StatusNotImplemented) + } + + if result := <-Srv.Store.Webhook().GetOutgoingByChannel(channelId, page*perPage, perPage); result.Err != nil { + return nil, result.Err + } else { + return result.Data.([]*model.OutgoingWebhook), nil + } +} + func GetOutgoingWebhooksForTeamPage(teamId string, page, perPage int) ([]*model.OutgoingWebhook, *model.AppError) { if !utils.Cfg.ServiceSettings.EnableOutgoingWebhooks { return nil, model.NewAppError("GetOutgoingWebhooksForTeamPage", "api.outgoing_webhook.disabled.app_error", nil, "", http.StatusNotImplemented) } - if result := <-Srv.Store.Webhook().GetOutgoingByTeam(teamId); result.Err != nil { + if result := <-Srv.Store.Webhook().GetOutgoingByTeam(teamId, page*perPage, perPage); result.Err != nil { return nil, result.Err } else { return result.Data.([]*model.OutgoingWebhook), nil diff --git a/model/client4.go b/model/client4.go index 94ac2c144..629bc69dd 100644 --- a/model/client4.go +++ b/model/client4.go @@ -162,6 +162,10 @@ func (c *Client4) GetComplianceReportRoute(reportId string) string { return fmt.Sprintf("/compliance/reports/%v", reportId) } +func (c *Client4) GetOutgoingWebhooksRoute() string { + return fmt.Sprintf("/hooks/outgoing") +} + func (c *Client4) GetPreferencesRoute(userId string) string { return fmt.Sprintf(c.GetUserRoute(userId) + "/preferences") } @@ -1125,6 +1129,49 @@ func (c *Client4) DeleteIncomingWebhook(hookID string) (bool, *Response) { } } +// CreateOutgoingWebhook creates an outgoing webhook for a team or channel. +func (c *Client4) CreateOutgoingWebhook(hook *OutgoingWebhook) (*OutgoingWebhook, *Response) { + if r, err := c.DoApiPost(c.GetOutgoingWebhooksRoute(), hook.ToJson()); err != nil { + return nil, &Response{StatusCode: r.StatusCode, Error: err} + } else { + defer closeBody(r) + return OutgoingWebhookFromJson(r.Body), BuildResponse(r) + } +} + +// GetOutgoingWebhooks returns a page of outgoing webhooks on the system. Page counting starts at 0. +func (c *Client4) GetOutgoingWebhooks(page int, perPage int, etag string) ([]*OutgoingWebhook, *Response) { + query := fmt.Sprintf("?page=%v&per_page=%v", page, perPage) + if r, err := c.DoApiGet(c.GetOutgoingWebhooksRoute()+query, etag); err != nil { + return nil, &Response{StatusCode: r.StatusCode, Error: err} + } else { + defer closeBody(r) + return OutgoingWebhookListFromJson(r.Body), BuildResponse(r) + } +} + +// GetOutgoingWebhooksForChannel returns a page of outgoing webhooks for a channel. Page counting starts at 0. +func (c *Client4) GetOutgoingWebhooksForChannel(channelId string, page int, perPage int, etag string) ([]*OutgoingWebhook, *Response) { + query := fmt.Sprintf("?page=%v&per_page=%v&channel_id=%v", page, perPage, channelId) + if r, err := c.DoApiGet(c.GetOutgoingWebhooksRoute()+query, etag); err != nil { + return nil, &Response{StatusCode: r.StatusCode, Error: err} + } else { + defer closeBody(r) + return OutgoingWebhookListFromJson(r.Body), BuildResponse(r) + } +} + +// GetOutgoingWebhooksForTeam returns a page of outgoing webhooks for a team. Page counting starts at 0. +func (c *Client4) GetOutgoingWebhooksForTeam(teamId string, page int, perPage int, etag string) ([]*OutgoingWebhook, *Response) { + query := fmt.Sprintf("?page=%v&per_page=%v&team_id=%v", page, perPage, teamId) + if r, err := c.DoApiGet(c.GetOutgoingWebhooksRoute()+query, etag); err != nil { + return nil, &Response{StatusCode: r.StatusCode, Error: err} + } else { + defer closeBody(r) + return OutgoingWebhookListFromJson(r.Body), BuildResponse(r) + } +} + // Preferences Section // GetPreferences returns the user's preferences. diff --git a/store/sql_channel_store.go b/store/sql_channel_store.go index 0ee6ae308..a85bb0656 100644 --- a/store/sql_channel_store.go +++ b/store/sql_channel_store.go @@ -386,9 +386,9 @@ 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()) + result.Err = model.NewAppError("SqlChannelStore.Get", "store.sql_channel.get.find.app_error", nil, "id="+id+", "+err.Error(), http.StatusInternalServerError) } else if obj == nil { - result.Err = model.NewAppError("SqlChannelStore.Get", "store.sql_channel.get.existing.app_error", nil, "id="+id, http.StatusBadRequest) + result.Err = model.NewAppError("SqlChannelStore.Get", "store.sql_channel.get.existing.app_error", nil, "id="+id, http.StatusNotFound) } 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 e0e6562bf..67290c983 100644 --- a/store/sql_webhook_store.go +++ b/store/sql_webhook_store.go @@ -329,7 +329,7 @@ func (s SqlWebhookStore) GetOutgoing(id string) StoreChannel { return storeChannel } -func (s SqlWebhookStore) GetOutgoingByChannel(channelId string) StoreChannel { +func (s SqlWebhookStore) GetOutgoingList(offset, limit int) StoreChannel { storeChannel := make(StoreChannel, 1) go func() { @@ -337,7 +337,35 @@ func (s SqlWebhookStore) GetOutgoingByChannel(channelId string) StoreChannel { var webhooks []*model.OutgoingWebhook - if _, err := s.GetReplica().Select(&webhooks, "SELECT * FROM OutgoingWebhooks WHERE ChannelId = :ChannelId AND DeleteAt = 0", map[string]interface{}{"ChannelId": channelId}); err != nil { + if _, err := s.GetReplica().Select(&webhooks, "SELECT * FROM OutgoingWebhooks WHERE DeleteAt = 0 LIMIT :Limit OFFSET :Offset", map[string]interface{}{"Offset": offset, "Limit": limit}); err != nil { + result.Err = model.NewLocAppError("SqlWebhookStore.GetOutgoingList", "store.sql_webhooks.get_outgoing_by_channel.app_error", nil, "err="+err.Error()) + } + + result.Data = webhooks + + storeChannel <- result + close(storeChannel) + }() + + return storeChannel +} + +func (s SqlWebhookStore) GetOutgoingByChannel(channelId string, offset, limit int) StoreChannel { + storeChannel := make(StoreChannel, 1) + + go func() { + result := StoreResult{} + + var webhooks []*model.OutgoingWebhook + + query := "" + if limit < 0 || offset < 0 { + query = "SELECT * FROM OutgoingWebhooks WHERE ChannelId = :ChannelId AND DeleteAt = 0" + } else { + query = "SELECT * FROM OutgoingWebhooks WHERE ChannelId = :ChannelId AND DeleteAt = 0 LIMIT :Limit OFFSET :Offset" + } + + if _, err := s.GetReplica().Select(&webhooks, query, map[string]interface{}{"ChannelId": channelId, "Offset": offset, "Limit": limit}); err != nil { result.Err = model.NewLocAppError("SqlWebhookStore.GetOutgoingByChannel", "store.sql_webhooks.get_outgoing_by_channel.app_error", nil, "channelId="+channelId+", err="+err.Error()) } @@ -350,7 +378,7 @@ func (s SqlWebhookStore) GetOutgoingByChannel(channelId string) StoreChannel { return storeChannel } -func (s SqlWebhookStore) GetOutgoingByTeam(teamId string) StoreChannel { +func (s SqlWebhookStore) GetOutgoingByTeam(teamId string, offset, limit int) StoreChannel { storeChannel := make(StoreChannel, 1) go func() { @@ -358,7 +386,14 @@ func (s SqlWebhookStore) GetOutgoingByTeam(teamId string) StoreChannel { var webhooks []*model.OutgoingWebhook - if _, err := s.GetReplica().Select(&webhooks, "SELECT * FROM OutgoingWebhooks WHERE TeamId = :TeamId AND DeleteAt = 0", map[string]interface{}{"TeamId": teamId}); err != nil { + query := "" + if limit < 0 || offset < 0 { + query = "SELECT * FROM OutgoingWebhooks WHERE TeamId = :TeamId AND DeleteAt = 0" + } else { + query = "SELECT * FROM OutgoingWebhooks WHERE TeamId = :TeamId AND DeleteAt = 0 LIMIT :Limit OFFSET :Offset" + } + + if _, err := s.GetReplica().Select(&webhooks, query, map[string]interface{}{"TeamId": teamId, "Offset": offset, "Limit": limit}); err != nil { result.Err = model.NewLocAppError("SqlWebhookStore.GetOutgoingByTeam", "store.sql_webhooks.get_outgoing_by_team.app_error", nil, "teamId="+teamId+", err="+err.Error()) } diff --git a/store/sql_webhook_store_test.go b/store/sql_webhook_store_test.go index 20bb8c151..6cfe36450 100644 --- a/store/sql_webhook_store_test.go +++ b/store/sql_webhook_store_test.go @@ -240,6 +240,59 @@ func TestWebhookStoreGetOutgoing(t *testing.T) { } } +func TestWebhookStoreGetOutgoingList(t *testing.T) { + Setup() + + o1 := &model.OutgoingWebhook{} + o1.ChannelId = model.NewId() + o1.CreatorId = model.NewId() + o1.TeamId = model.NewId() + o1.CallbackURLs = []string{"http://nowhere.com/"} + + o1 = (<-store.Webhook().SaveOutgoing(o1)).Data.(*model.OutgoingWebhook) + + o2 := &model.OutgoingWebhook{} + o2.ChannelId = model.NewId() + o2.CreatorId = model.NewId() + o2.TeamId = model.NewId() + o2.CallbackURLs = []string{"http://nowhere.com/"} + + o2 = (<-store.Webhook().SaveOutgoing(o2)).Data.(*model.OutgoingWebhook) + + if r1 := <-store.Webhook().GetOutgoingList(0, 1000); r1.Err != nil { + t.Fatal(r1.Err) + } else { + hooks := r1.Data.([]*model.OutgoingWebhook) + found1 := false + found2 := false + + for _, hook := range hooks { + if hook.CreateAt != o1.CreateAt { + found1 = true + } + + if hook.CreateAt != o2.CreateAt { + found2 = true + } + } + + if !found1 { + t.Fatal("missing hook1") + } + if !found2 { + t.Fatal("missing hook2") + } + } + + if result := <-store.Webhook().GetOutgoingList(0, 2); result.Err != nil { + t.Fatal(result.Err) + } else { + if len(result.Data.([]*model.OutgoingWebhook)) != 2 { + t.Fatal("wrong number of hooks returned") + } + } +} + func TestWebhookStoreGetOutgoingByChannel(t *testing.T) { Setup() @@ -251,7 +304,7 @@ func TestWebhookStoreGetOutgoingByChannel(t *testing.T) { o1 = (<-store.Webhook().SaveOutgoing(o1)).Data.(*model.OutgoingWebhook) - if r1 := <-store.Webhook().GetOutgoingByChannel(o1.ChannelId); r1.Err != nil { + if r1 := <-store.Webhook().GetOutgoingByChannel(o1.ChannelId, 0, 100); r1.Err != nil { t.Fatal(r1.Err) } else { if r1.Data.([]*model.OutgoingWebhook)[0].CreateAt != o1.CreateAt { @@ -259,7 +312,7 @@ func TestWebhookStoreGetOutgoingByChannel(t *testing.T) { } } - if result := <-store.Webhook().GetOutgoingByChannel("123"); result.Err != nil { + if result := <-store.Webhook().GetOutgoingByChannel("123", -1, -1); result.Err != nil { t.Fatal(result.Err) } else { if len(result.Data.([]*model.OutgoingWebhook)) != 0 { @@ -279,7 +332,7 @@ func TestWebhookStoreGetOutgoingByTeam(t *testing.T) { o1 = (<-store.Webhook().SaveOutgoing(o1)).Data.(*model.OutgoingWebhook) - if r1 := <-store.Webhook().GetOutgoingByTeam(o1.TeamId); r1.Err != nil { + if r1 := <-store.Webhook().GetOutgoingByTeam(o1.TeamId, 0, 100); r1.Err != nil { t.Fatal(r1.Err) } else { if r1.Data.([]*model.OutgoingWebhook)[0].CreateAt != o1.CreateAt { @@ -287,7 +340,7 @@ func TestWebhookStoreGetOutgoingByTeam(t *testing.T) { } } - if result := <-store.Webhook().GetOutgoingByTeam("123"); result.Err != nil { + if result := <-store.Webhook().GetOutgoingByTeam("123", -1, -1); result.Err != nil { t.Fatal(result.Err) } else { if len(result.Data.([]*model.OutgoingWebhook)) != 0 { diff --git a/store/store.go b/store/store.go index 5596fa7f8..497f613da 100644 --- a/store/store.go +++ b/store/store.go @@ -273,8 +273,9 @@ type WebhookStore interface { SaveOutgoing(webhook *model.OutgoingWebhook) StoreChannel GetOutgoing(id string) StoreChannel - GetOutgoingByChannel(channelId string) StoreChannel - GetOutgoingByTeam(teamId string) StoreChannel + GetOutgoingList(offset, limit int) StoreChannel + GetOutgoingByChannel(channelId string, offset, limit int) StoreChannel + GetOutgoingByTeam(teamId string, offset, limit int) StoreChannel DeleteOutgoing(webhookId string, time int64) StoreChannel PermanentDeleteOutgoingByUser(userId string) StoreChannel UpdateOutgoing(hook *model.OutgoingWebhook) StoreChannel -- cgit v1.2.3-1-g7c22