diff --git a/api4/job.go b/api4/job.go index bd28ea2c86..6e0ae75623 100644 --- a/api4/job.go +++ b/api4/job.go @@ -4,8 +4,8 @@ package api4 import ( - "fmt" "net/http" + "path/filepath" "strconv" "time" @@ -45,17 +45,8 @@ func getJob(c *Context, w http.ResponseWriter, r *http.Request) { func downloadJob(c *Context, w http.ResponseWriter, r *http.Request) { config := c.App.Config() - const FILE_PATH = "export/%s/%s" - fileInfo := map[string]map[string]string{ - "csv": { - "fileName": "csv_export.zip", - "fileMime": "application/zip", - }, - "actiance": { - "fileName": "actiance_export.zip", - "fileMime": "application/zip", - }, - } + const FILE_PATH = "export" + const FILE_MIME = "application/zip" c.RequireJobId() if c.Err != nil { @@ -85,7 +76,8 @@ func downloadJob(c *Context, w http.ResponseWriter, r *http.Request) { return } - filePath := fmt.Sprintf(FILE_PATH, job.Id, fileInfo[job.Data["export_type"]]["fileName"]) + fileName := job.Id + ".zip" + filePath := filepath.Join(FILE_PATH, fileName) fileReader, err := c.App.FileReader(filePath) if err != nil { mlog.Error(err.Error()) @@ -97,7 +89,7 @@ func downloadJob(c *Context, w http.ResponseWriter, r *http.Request) { // We are able to pass 0 for content size due to the fact that Golang's serveContent (https://golang.org/src/net/http/fs.go) // already sets that for us - err = writeFileResponse(fileInfo[job.Data["export_type"]]["fileName"], fileInfo[job.Data["export_type"]]["fileMime"], 0, time.Unix(0, job.LastActivityAt*int64(1000*1000)), *c.App.Config().ServiceSettings.WebserverMode, fileReader, true, w, r) + err = writeFileResponse(fileName, FILE_MIME, 0, time.Unix(0, job.LastActivityAt*int64(1000*1000)), *c.App.Config().ServiceSettings.WebserverMode, fileReader, true, w, r) if err != nil { mlog.Error(err.Error()) c.Err = err diff --git a/api4/job_test.go b/api4/job_test.go index d507dc6fe1..896ec83630 100644 --- a/api4/job_test.go +++ b/api4/job_test.go @@ -4,6 +4,8 @@ package api4 import ( + "os" + "path/filepath" "strings" "testing" @@ -171,3 +173,109 @@ func TestGetJobsByType(t *testing.T) { _, resp = th.Client.GetJobsByType(jobType, 0, 60) CheckForbiddenStatus(t, resp) } + +func TestDownloadJob(t *testing.T) { + th := Setup(t).InitBasic() + defer th.TearDown() + jobName := model.NewId() + job := &model.Job{ + Id: jobName, + Type: model.JOB_TYPE_MESSAGE_EXPORT, + Data: map[string]string{ + "export_type": "csv", + }, + Status: model.JOB_STATUS_SUCCESS, + } + + // DownloadExportResults is not set to true so we should get a not implemented error status + _, resp := th.Client.DownloadJob(job.Id) + CheckNotImplementedStatus(t, resp) + + th.App.UpdateConfig(func(cfg *model.Config) { + *cfg.MessageExportSettings.DownloadExportResults = true + }) + + // Normal user cannot download the results of these job (Doesn't have permission) + _, resp = th.Client.DownloadJob(job.Id) + CheckForbiddenStatus(t, resp) + + // System admin trying to download the results of a non-existant job + _, resp = th.SystemAdminClient.DownloadJob(job.Id) + CheckNotFoundStatus(t, resp) + + // Here we have a job that exist in our database but the results do not exist therefore when we try to download the results + // as a system admin, we should get a not found status. + _, err := th.App.Srv().Store.Job().Save(job) + require.Nil(t, err) + defer th.App.Srv().Store.Job().Delete(job.Id) + + filePath := "./data/export/" + job.Id + "/testdat.txt" + mkdirAllErr := os.MkdirAll(filepath.Dir(filePath), 0770) + require.Nil(t, mkdirAllErr) + os.Create(filePath) + + _, resp = th.SystemAdminClient.DownloadJob(job.Id) + CheckBadRequestStatus(t, resp) + + job.Data["is_downloadable"] = "true" + updateStatus, err := th.App.Srv().Store.Job().UpdateOptimistically(job, model.JOB_STATUS_SUCCESS) + require.True(t, updateStatus) + require.Nil(t, err) + + _, resp = th.SystemAdminClient.DownloadJob(job.Id) + CheckNotFoundStatus(t, resp) + + // Now we stub the results of the job into the same directory and try to download it again + // This time we should successfully retrieve the results without any error + filePath = "./data/export/" + job.Id + ".zip" + mkdirAllErr = os.MkdirAll(filepath.Dir(filePath), 0770) + require.Nil(t, mkdirAllErr) + os.Create(filePath) + + _, resp = th.SystemAdminClient.DownloadJob(job.Id) + require.Nil(t, resp.Error) +} + +func TestCancelJob(t *testing.T) { + th := Setup(t) + defer th.TearDown() + + jobs := []*model.Job{ + { + Id: model.NewId(), + Type: model.NewId(), + Status: model.JOB_STATUS_PENDING, + }, + { + Id: model.NewId(), + Type: model.NewId(), + Status: model.JOB_STATUS_IN_PROGRESS, + }, + { + Id: model.NewId(), + Type: model.NewId(), + Status: model.JOB_STATUS_SUCCESS, + }, + } + + for _, job := range jobs { + _, err := th.App.Srv().Store.Job().Save(job) + require.Nil(t, err) + defer th.App.Srv().Store.Job().Delete(job.Id) + } + + _, resp := th.Client.CancelJob(jobs[0].Id) + CheckForbiddenStatus(t, resp) + + _, resp = th.SystemAdminClient.CancelJob(jobs[0].Id) + require.Nil(t, resp.Error) + + _, resp = th.SystemAdminClient.CancelJob(jobs[1].Id) + require.Nil(t, resp.Error) + + _, resp = th.SystemAdminClient.CancelJob(jobs[2].Id) + CheckInternalErrorStatus(t, resp) + + _, resp = th.SystemAdminClient.CancelJob(model.NewId()) + CheckInternalErrorStatus(t, resp) +}