summaryrefslogtreecommitdiffstats
path: root/api4
diff options
context:
space:
mode:
Diffstat (limited to 'api4')
-rw-r--r--api4/elasticsearch.go7
-rw-r--r--api4/job.go14
-rw-r--r--api4/job_test.go30
-rw-r--r--api4/post_test.go156
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()