github.com/justinjmoses/evergreen@v0.0.0-20170530173719-1d50e381ff0d/service/rest_task_history.go (about)

     1  package service
     2  
     3  import (
     4  	"fmt"
     5  	"net/http"
     6  	"time"
     7  
     8  	"github.com/evergreen-ci/evergreen"
     9  	"github.com/evergreen-ci/evergreen/model"
    10  	"github.com/evergreen-ci/evergreen/util"
    11  	"github.com/gorilla/mux"
    12  	"github.com/mongodb/grip"
    13  )
    14  
    15  const (
    16  	// Number of revisions to return in task history
    17  	MaxRestNumRevisions = 10
    18  )
    19  
    20  type RestTestHistoryResult struct {
    21  	TestFile     string        `json:"test_file" csv:"test_file"`
    22  	TaskName     string        `json:"task_name" csv:"task_name"`
    23  	TestStatus   string        `json:"test_status" csv:"test_status"`
    24  	TaskStatus   string        `json:"task_status" csv:"task_status"`
    25  	Revision     string        `json:"revision" csv:"revision"`
    26  	Project      string        `json:"project" csv:"project"`
    27  	TaskId       string        `json:"task_id" csv:"task_id"`
    28  	BuildVariant string        `json:"variant" csv:"variant"`
    29  	StartTime    time.Time     `json:"start_time" csv:"start_time"`
    30  	EndTime      time.Time     `json:"end_time" csv:"end_time"`
    31  	DurationMS   time.Duration `json:"duration" csv:"duration"`
    32  	Execution    int           `json:"execution" csv:"execution"`
    33  	Url          string        `json:"url" csv:"url"`
    34  	UrlRaw       string        `json:"url_raw" csv:"url_raw"`
    35  }
    36  
    37  func (restapi restAPI) getTaskHistory(w http.ResponseWriter, r *http.Request) {
    38  	taskName := mux.Vars(r)["task_name"]
    39  	projCtx := MustHaveRESTContext(r)
    40  	project := projCtx.Project
    41  	if project == nil {
    42  		restapi.WriteJSON(w, http.StatusInternalServerError, responseError{Message: "error loading project"})
    43  		return
    44  	}
    45  
    46  	buildVariants := project.GetVariantsWithTask(taskName)
    47  	iter := model.NewTaskHistoryIterator(taskName, buildVariants, project.Identifier)
    48  
    49  	chunk, err := iter.GetChunk(nil, MaxRestNumRevisions, NoRevisions, false)
    50  	if err != nil {
    51  		msg := fmt.Sprintf("Error finding history for task '%v'", taskName)
    52  		grip.Errorf("%v: %+v", msg, err)
    53  		restapi.WriteJSON(w, http.StatusInternalServerError, responseError{Message: msg})
    54  		return
    55  	}
    56  
    57  	restapi.WriteJSON(w, http.StatusOK, chunk)
    58  	return
    59  
    60  }
    61  
    62  // logURL returns the full URL for linking to a test's logs.
    63  // Returns the empty string if no internal or external log is referenced.
    64  func logURL(url, logId, root string) string {
    65  	if logId != "" {
    66  		return root + "/test_log/" + logId
    67  	}
    68  	return url
    69  }
    70  
    71  // getTestHistory retrieves the test history query parameters from the request
    72  // and passes them to the function that gets the test results.
    73  func (restapi restAPI) GetTestHistory(w http.ResponseWriter, r *http.Request) {
    74  	projectId := mux.Vars(r)["project_id"]
    75  	if projectId == "" {
    76  		restapi.WriteJSON(w, http.StatusInternalServerError, responseError{Message: "invalid project id"})
    77  		return
    78  	}
    79  	params := model.TestHistoryParameters{}
    80  	params.Project = projectId
    81  	params.TaskNames = util.GetStringArrayValue(r, "tasks", []string{})
    82  	params.TestNames = util.GetStringArrayValue(r, "tests", []string{})
    83  	params.BuildVariants = util.GetStringArrayValue(r, "variants", []string{})
    84  	params.TestStatuses = util.GetStringArrayValue(r, "testStatuses", []string{})
    85  	params.TaskStatuses = util.GetStringArrayValue(r, "taskStatuses", []string{})
    86  
    87  	var err error
    88  	params.Limit, err = util.GetIntValue(r, "limit", 0)
    89  	if err != nil {
    90  		restapi.WriteJSON(w, http.StatusBadRequest, "invalid value for field 'limit'")
    91  		return
    92  	}
    93  
    94  	if len(params.TaskStatuses) == 0 {
    95  		params.TaskStatuses = []string{evergreen.TaskFailed}
    96  	}
    97  	if len(params.TestStatuses) == 0 {
    98  		params.TestStatuses = []string{evergreen.TestFailedStatus}
    99  	}
   100  
   101  	params.BeforeRevision = r.FormValue("beforeRevision")
   102  	params.AfterRevision = r.FormValue("afterRevision")
   103  
   104  	beforeDate := r.FormValue("beforeDate")
   105  	if beforeDate != "" {
   106  		params.BeforeDate, err = time.Parse(time.RFC3339, beforeDate)
   107  		if err != nil {
   108  			restapi.WriteJSON(w, http.StatusBadRequest, "invalid format for field 'before date'")
   109  			return
   110  		}
   111  	}
   112  
   113  	afterDate := r.FormValue("afterDate")
   114  	if afterDate != "" {
   115  		params.AfterDate, err = time.Parse(time.RFC3339, afterDate)
   116  		if err != nil {
   117  			restapi.WriteJSON(w, http.StatusBadRequest, "invalid format for field 'after date'")
   118  			return
   119  		}
   120  	}
   121  
   122  	sort := r.FormValue("sort")
   123  	switch sort {
   124  	case "earliest":
   125  		params.Sort = 1
   126  	case "", "latest":
   127  		params.Sort = -1
   128  	default:
   129  		restapi.WriteJSON(w, http.StatusBadRequest, "invalid sort, must be earliest or latest")
   130  		return
   131  	}
   132  
   133  	// export format
   134  	isCSV, err := util.GetBoolValue(r, "csv", false)
   135  	if err != nil {
   136  		restapi.WriteJSON(w, http.StatusBadRequest, err.Error())
   137  		return
   138  	}
   139  
   140  	err = params.SetDefaultsAndValidate()
   141  	if err != nil {
   142  		restapi.WriteJSON(w, http.StatusBadRequest, err.Error())
   143  		return
   144  	}
   145  	results, err := model.GetTestHistory(&params)
   146  	if err != nil {
   147  		restapi.WriteJSON(w, http.StatusBadRequest, err.Error())
   148  		return
   149  	}
   150  	restHistoryResults := []RestTestHistoryResult{}
   151  	for _, result := range results {
   152  		startTime := time.Unix(int64(result.StartTime), 0)
   153  		endTime := time.Unix(int64(result.EndTime), 0)
   154  		taskStatus := result.TaskStatus
   155  		if result.TaskStatus == evergreen.TaskFailed {
   156  			if result.TaskTimedOut {
   157  				taskStatus = model.TaskTimeout
   158  			}
   159  			if result.TaskDetailsType == "system" {
   160  				taskStatus = model.TaskSystemFailure
   161  			}
   162  		}
   163  		url := logURL(result.Url, result.LogId, restapi.GetSettings().Ui.Url)
   164  		restHistoryResults = append(restHistoryResults, RestTestHistoryResult{
   165  			TestFile:     result.TestFile,
   166  			TaskName:     result.TaskName,
   167  			TestStatus:   result.TestStatus,
   168  			TaskStatus:   taskStatus,
   169  			Revision:     result.Revision,
   170  			Project:      result.Project,
   171  			TaskId:       result.TaskId,
   172  			BuildVariant: result.BuildVariant,
   173  			StartTime:    startTime,
   174  			EndTime:      endTime,
   175  			DurationMS:   endTime.Sub(startTime),
   176  			Url:          url,
   177  			UrlRaw:       result.UrlRaw,
   178  			Execution:    result.Execution,
   179  		})
   180  	}
   181  	if isCSV {
   182  		util.WriteCSVResponse(w, http.StatusOK, restHistoryResults)
   183  		return
   184  	}
   185  	restapi.WriteJSON(w, http.StatusOK, restHistoryResults)
   186  
   187  }