diff options
Diffstat (limited to 'api4')
-rw-r--r-- | api4/elasticsearch.go | 7 | ||||
-rw-r--r-- | api4/job.go | 14 | ||||
-rw-r--r-- | api4/job_test.go | 30 | ||||
-rw-r--r-- | api4/post_test.go | 156 |
4 files changed, 184 insertions, 23 deletions
diff --git a/api4/elasticsearch.go b/api4/elasticsearch.go index 05ef1f539..9eafec48b 100644 --- a/api4/elasticsearch.go +++ b/api4/elasticsearch.go @@ -19,12 +19,17 @@ func InitElasticsearch() { } func testElasticsearch(c *Context, w http.ResponseWriter, r *http.Request) { + cfg := model.ConfigFromJson(r.Body) + if cfg == nil { + cfg = utils.Cfg + } + if !app.SessionHasPermissionTo(c.Session, model.PERMISSION_MANAGE_SYSTEM) { c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM) return } - if err := app.TestElasticsearch(); err != nil { + if err := app.TestElasticsearch(cfg); err != nil { c.Err = err return } diff --git a/api4/job.go b/api4/job.go index 8610d9e74..e6c17c42d 100644 --- a/api4/job.go +++ b/api4/job.go @@ -14,11 +14,11 @@ import ( func InitJob() { l4g.Info("Initializing job API routes") - BaseRoutes.Jobs.Handle("/type/{job_type:[A-Za-z0-9_-]+}/statuses", ApiSessionRequired(getJobStatusesByType)).Methods("GET") - BaseRoutes.Jobs.Handle("/{job_id:[A-Za-z0-9]+}/status", ApiSessionRequired(getJobStatus)).Methods("GET") + BaseRoutes.Jobs.Handle("/type/{job_type:[A-Za-z0-9_-]+}/statuses", ApiSessionRequired(getJobsByType)).Methods("GET") + BaseRoutes.Jobs.Handle("/{job_id:[A-Za-z0-9]+}/status", ApiSessionRequired(getJob)).Methods("GET") } -func getJobStatus(c *Context, w http.ResponseWriter, r *http.Request) { +func getJob(c *Context, w http.ResponseWriter, r *http.Request) { c.RequireJobId() if c.Err != nil { return @@ -29,7 +29,7 @@ func getJobStatus(c *Context, w http.ResponseWriter, r *http.Request) { return } - if status, err := app.GetJobStatus(c.Params.JobId); err != nil { + if status, err := app.GetJob(c.Params.JobId); err != nil { c.Err = err return } else { @@ -37,7 +37,7 @@ func getJobStatus(c *Context, w http.ResponseWriter, r *http.Request) { } } -func getJobStatusesByType(c *Context, w http.ResponseWriter, r *http.Request) { +func getJobsByType(c *Context, w http.ResponseWriter, r *http.Request) { c.RequireJobType() if c.Err != nil { return @@ -48,10 +48,10 @@ func getJobStatusesByType(c *Context, w http.ResponseWriter, r *http.Request) { return } - if statuses, err := app.GetJobStatusesByTypePage(c.Params.JobType, c.Params.Page, c.Params.PerPage); err != nil { + if statuses, err := app.GetJobsByTypePage(c.Params.JobType, c.Params.Page, c.Params.PerPage); err != nil { c.Err = err return } else { - w.Write([]byte(model.JobStatusesToJson(statuses))) + w.Write([]byte(model.JobsToJson(statuses))) } } diff --git a/api4/job_test.go b/api4/job_test.go index 0f39fc306..8bbea83e1 100644 --- a/api4/job_test.go +++ b/api4/job_test.go @@ -16,30 +16,30 @@ func TestGetJobStatus(t *testing.T) { th := Setup().InitBasic().InitSystemAdmin() defer TearDown() - status := &model.JobStatus{ + status := &model.Job{ Id: model.NewId(), Status: model.NewId(), } - if result := <-app.Srv.Store.JobStatus().SaveOrUpdate(status); result.Err != nil { + if result := <-app.Srv.Store.Job().Save(status); result.Err != nil { t.Fatal(result.Err) } - defer app.Srv.Store.JobStatus().Delete(status.Id) + defer app.Srv.Store.Job().Delete(status.Id) - received, resp := th.SystemAdminClient.GetJobStatus(status.Id) + received, resp := th.SystemAdminClient.GetJob(status.Id) CheckNoError(t, resp) if received.Id != status.Id || received.Status != status.Status { t.Fatal("incorrect job status received") } - _, resp = th.SystemAdminClient.GetJobStatus("1234") + _, resp = th.SystemAdminClient.GetJob("1234") CheckBadRequestStatus(t, resp) - _, resp = th.Client.GetJobStatus(status.Id) + _, resp = th.Client.GetJob(status.Id) CheckForbiddenStatus(t, resp) - _, resp = th.SystemAdminClient.GetJobStatus(model.NewId()) + _, resp = th.SystemAdminClient.GetJob(model.NewId()) CheckNotFoundStatus(t, resp) } @@ -49,7 +49,7 @@ func TestGetJobStatusesByType(t *testing.T) { jobType := model.NewId() - statuses := []*model.JobStatus{ + statuses := []*model.Job{ { Id: model.NewId(), Type: jobType, @@ -68,11 +68,11 @@ func TestGetJobStatusesByType(t *testing.T) { } for _, status := range statuses { - store.Must(app.Srv.Store.JobStatus().SaveOrUpdate(status)) - defer app.Srv.Store.JobStatus().Delete(status.Id) + store.Must(app.Srv.Store.Job().Save(status)) + defer app.Srv.Store.Job().Delete(status.Id) } - received, resp := th.SystemAdminClient.GetJobStatusesByType(jobType, 0, 2) + received, resp := th.SystemAdminClient.GetJobsByType(jobType, 0, 2) CheckNoError(t, resp) if len(received) != 2 { @@ -83,7 +83,7 @@ func TestGetJobStatusesByType(t *testing.T) { t.Fatal("should've received second newest job second") } - received, resp = th.SystemAdminClient.GetJobStatusesByType(jobType, 1, 2) + received, resp = th.SystemAdminClient.GetJobsByType(jobType, 1, 2) CheckNoError(t, resp) if len(received) != 1 { @@ -92,12 +92,12 @@ func TestGetJobStatusesByType(t *testing.T) { t.Fatal("should've received oldest job last") } - _, resp = th.SystemAdminClient.GetJobStatusesByType("", 0, 60) + _, resp = th.SystemAdminClient.GetJobsByType("", 0, 60) CheckNotFoundStatus(t, resp) - _, resp = th.SystemAdminClient.GetJobStatusesByType(strings.Repeat("a", 33), 0, 60) + _, resp = th.SystemAdminClient.GetJobsByType(strings.Repeat("a", 33), 0, 60) CheckBadRequestStatus(t, resp) - _, resp = th.Client.GetJobStatusesByType(jobType, 0, 60) + _, resp = th.Client.GetJobsByType(jobType, 0, 60) CheckForbiddenStatus(t, resp) } diff --git a/api4/post_test.go b/api4/post_test.go index a2c0b065b..d554ca472 100644 --- a/api4/post_test.go +++ b/api4/post_test.go @@ -4,9 +4,13 @@ package api4 import ( + "encoding/json" "net/http" + "net/http/httptest" + "net/url" "reflect" "strconv" + "strings" "testing" "time" @@ -101,6 +105,158 @@ func TestCreatePost(t *testing.T) { } } +func testCreatePostWithOutgoingHook( + t *testing.T, + hookContentType, expectedContentType, message, triggerWord string, + fileIds []string, + triggerWhen int, +) { + th := Setup().InitBasic().InitSystemAdmin() + defer TearDown() + user := th.SystemAdminUser + team := th.BasicTeam + channel := th.BasicChannel + + 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() + + var hook *model.OutgoingWebhook + var post *model.Post + + // Create a test server that is the target of the outgoing webhook. It will + // validate the webhook body fields and write to the success channel on + // success/failure. + success := make(chan bool) + wait := make(chan bool, 1) + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + <-wait + + requestContentType := r.Header.Get("Content-Type") + if requestContentType != expectedContentType { + t.Logf("Content-Type is %s, should be %s", requestContentType, expectedContentType) + success <- false + return + } + + expectedPayload := &model.OutgoingWebhookPayload{ + Token: hook.Token, + TeamId: hook.TeamId, + TeamDomain: team.Name, + ChannelId: post.ChannelId, + ChannelName: channel.Name, + Timestamp: post.CreateAt, + UserId: post.UserId, + UserName: user.Username, + PostId: post.Id, + Text: post.Message, + TriggerWord: triggerWord, + FileIds: strings.Join(post.FileIds, ","), + } + + // depending on the Content-Type, we expect to find a JSON or form encoded payload + if requestContentType == "application/json" { + decoder := json.NewDecoder(r.Body) + o := &model.OutgoingWebhookPayload{} + decoder.Decode(&o) + + if !reflect.DeepEqual(expectedPayload, o) { + t.Logf("JSON payload is %+v, should be %+v", o, expectedPayload) + success <- false + return + } + } else { + err := r.ParseForm() + if err != nil { + t.Logf("Error parsing form: %q", err) + success <- false + return + } + + expectedFormValues, _ := url.ParseQuery(expectedPayload.ToFormValues()) + if !reflect.DeepEqual(expectedFormValues, r.Form) { + t.Logf("Form values are: %q\n, should be: %q\n", r.Form, expectedFormValues) + success <- false + return + } + } + + success <- true + })) + defer ts.Close() + + // create an outgoing webhook, passing it the test server URL + var triggerWords []string + if triggerWord != "" { + triggerWords = []string{triggerWord} + } + + hook = &model.OutgoingWebhook{ + ChannelId: channel.Id, + TeamId: team.Id, + ContentType: hookContentType, + TriggerWords: triggerWords, + TriggerWhen: triggerWhen, + CallbackURLs: []string{ts.URL}, + } + + hook, resp := th.SystemAdminClient.CreateOutgoingWebhook(hook) + CheckNoError(t, resp) + + // create a post to trigger the webhook + post = &model.Post{ + ChannelId: channel.Id, + Message: message, + FileIds: fileIds, + } + + post, resp = th.SystemAdminClient.CreatePost(post) + CheckNoError(t, resp) + + wait <- true + + // We wait for the test server to write to the success channel and we make + // the test fail if that doesn't happen before the timeout. + select { + case ok := <-success: + if !ok { + t.Fatal("Test server did send an invalid webhook.") + } + case <-time.After(time.Second): + t.Fatal("Timeout, test server did not send the webhook.") + } +} + +func TestCreatePostWithOutgoingHook_form_urlencoded(t *testing.T) { + testCreatePostWithOutgoingHook(t, "application/x-www-form-urlencoded", "application/x-www-form-urlencoded", "triggerword lorem ipsum", "triggerword", []string{"file_id_1"}, app.TRIGGERWORDS_EXACT_MATCH) + testCreatePostWithOutgoingHook(t, "application/x-www-form-urlencoded", "application/x-www-form-urlencoded", "triggerwordaaazzz lorem ipsum", "triggerword", []string{"file_id_1"}, app.TRIGGERWORDS_STARTS_WITH) + testCreatePostWithOutgoingHook(t, "application/x-www-form-urlencoded", "application/x-www-form-urlencoded", "", "", []string{"file_id_1"}, app.TRIGGERWORDS_EXACT_MATCH) + testCreatePostWithOutgoingHook(t, "application/x-www-form-urlencoded", "application/x-www-form-urlencoded", "", "", []string{"file_id_1"}, app.TRIGGERWORDS_STARTS_WITH) +} + +func TestCreatePostWithOutgoingHook_json(t *testing.T) { + testCreatePostWithOutgoingHook(t, "application/json", "application/json", "triggerword lorem ipsum", "triggerword", []string{"file_id_1, file_id_2"}, app.TRIGGERWORDS_EXACT_MATCH) + testCreatePostWithOutgoingHook(t, "application/json", "application/json", "triggerwordaaazzz lorem ipsum", "triggerword", []string{"file_id_1, file_id_2"}, app.TRIGGERWORDS_STARTS_WITH) + testCreatePostWithOutgoingHook(t, "application/json", "application/json", "triggerword lorem ipsum", "", []string{"file_id_1"}, app.TRIGGERWORDS_EXACT_MATCH) + testCreatePostWithOutgoingHook(t, "application/json", "application/json", "triggerwordaaazzz lorem ipsum", "", []string{"file_id_1"}, app.TRIGGERWORDS_STARTS_WITH) +} + +// hooks created before we added the ContentType field should be considered as +// application/x-www-form-urlencoded +func TestCreatePostWithOutgoingHook_no_content_type(t *testing.T) { + testCreatePostWithOutgoingHook(t, "", "application/x-www-form-urlencoded", "triggerword lorem ipsum", "triggerword", []string{"file_id_1"}, app.TRIGGERWORDS_EXACT_MATCH) + testCreatePostWithOutgoingHook(t, "", "application/x-www-form-urlencoded", "triggerwordaaazzz lorem ipsum", "triggerword", []string{"file_id_1"}, app.TRIGGERWORDS_STARTS_WITH) + testCreatePostWithOutgoingHook(t, "", "application/x-www-form-urlencoded", "triggerword lorem ipsum", "", []string{"file_id_1, file_id_2"}, app.TRIGGERWORDS_EXACT_MATCH) + testCreatePostWithOutgoingHook(t, "", "application/x-www-form-urlencoded", "triggerwordaaazzz lorem ipsum", "", []string{"file_id_1, file_id_2"}, app.TRIGGERWORDS_STARTS_WITH) +} + func TestUpdatePost(t *testing.T) { th := Setup().InitBasic().InitSystemAdmin() defer TearDown() |