Skip to content

Commit

Permalink
Implement GET /hooks/incoming endpoint for APIv4
Browse files Browse the repository at this point in the history
  • Loading branch information
jwilander committed Feb 15, 2017
1 parent 7f00ef4 commit e67bc60
Show file tree
Hide file tree
Showing 8 changed files with 225 additions and 18 deletions.
14 changes: 3 additions & 11 deletions api/webhook.go
Expand Up @@ -117,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)))
}
}
Expand Down
31 changes: 31 additions & 0 deletions api4/webhook.go
Expand Up @@ -16,6 +16,7 @@ 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) {
Expand Down Expand Up @@ -52,3 +53,33 @@ func createIncomingHook(c *Context, w http.ResponseWriter, r *http.Request) {
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)))
}
84 changes: 83 additions & 1 deletion api4/webhook_test.go
Expand Up @@ -45,7 +45,7 @@ func TestCreateIncomingWebhook(t *testing.T) {

hook.ChannelId = "junk"
_, resp = th.SystemAdminClient.CreateIncomingWebhook(hook)
CheckBadRequestStatus(t, resp)
CheckNotFoundStatus(t, resp)

hook.ChannelId = th.BasicChannel.Id
th.LoginTeamAdmin()
Expand All @@ -66,3 +66,85 @@ func TestCreateIncomingWebhook(t *testing.T) {
_, 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)
}
26 changes: 25 additions & 1 deletion app/webhook.go
Expand Up @@ -195,7 +195,7 @@ func CreateWebhookPost(userId, teamId, channelId, text, overrideUsername, overri

func CreateIncomingWebhookForChannel(userId string, channel *model.Channel, hook *model.IncomingWebhook) (*model.IncomingWebhook, *model.AppError) {
if !utils.Cfg.ServiceSettings.EnableIncomingWebhooks {
return nil, model.NewAppError("createIncomingHook", "api.webhook.create_incoming.disabled.app_errror", nil, "", http.StatusNotImplemented)
return nil, model.NewAppError("CreateIncomingWebhookForChannel", "api.webhook.create_incoming.disabled.app_errror", nil, "", http.StatusNotImplemented)
}

hook.UserId = userId
Expand All @@ -207,3 +207,27 @@ func CreateIncomingWebhookForChannel(userId string, channel *model.Channel, hook
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("GetIncomingWebhooksForTeamPage", "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
}
}
22 changes: 22 additions & 0 deletions model/client4.go
Expand Up @@ -578,3 +578,25 @@ func (c *Client4) CreateIncomingWebhook(hook *IncomingWebhook) (*IncomingWebhook
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)
}
}
25 changes: 23 additions & 2 deletions store/sql_webhook_store.go
Expand Up @@ -183,15 +183,36 @@ 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 TeamId = :TeamId AND DeleteAt = 0", map[string]interface{}{"TeamId": teamId}); err != nil {
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.NewLocAppError("SqlWebhookStore.GetIncomingList", "store.sql_webhooks.get_incoming_by_user.app_error", nil, "err="+err.Error())
}

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() {
result := StoreResult{}

var webhooks []*model.IncomingWebhook

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.NewLocAppError("SqlWebhookStore.GetIncomingByUser", "store.sql_webhooks.get_incoming_by_user.app_error", nil, "teamId="+teamId+", err="+err.Error())
}

Expand Down
38 changes: 36 additions & 2 deletions store/sql_webhook_store_test.go
Expand Up @@ -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()

Expand All @@ -70,15 +104,15 @@ 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 {
t.Fatal("invalid returned webhook")
}
}

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 {
Expand Down
3 changes: 2 additions & 1 deletion store/store.go
Expand Up @@ -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
Expand Down

0 comments on commit e67bc60

Please sign in to comment.