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

     1  package service
     2  
     3  import (
     4  	"fmt"
     5  	"net/http"
     6  
     7  	"github.com/evergreen-ci/evergreen"
     8  	"github.com/evergreen-ci/evergreen/model"
     9  	"github.com/gorilla/context"
    10  	"github.com/gorilla/mux"
    11  	"github.com/pkg/errors"
    12  )
    13  
    14  // restContextKey is the type used to store
    15  type restContextKey int
    16  
    17  const RestContext restContextKey = 0
    18  
    19  type restAPIService interface {
    20  	WriteJSON(w http.ResponseWriter, status int, data interface{})
    21  	GetSettings() evergreen.Settings
    22  	LoggedError(http.ResponseWriter, *http.Request, int, error)
    23  }
    24  
    25  type restAPI struct {
    26  	restAPIService
    27  }
    28  
    29  // loadCtx is a pre-request wrapper function that populates a model.Context from request vars,
    30  // and attaches it to the request.
    31  func (ra *restAPI) loadCtx(next http.HandlerFunc) http.HandlerFunc {
    32  	return func(w http.ResponseWriter, r *http.Request) {
    33  		vars := mux.Vars(r)
    34  		taskId := vars["task_id"]
    35  		buildId := vars["build_id"]
    36  		versionId := vars["version_id"]
    37  		patchId := vars["patch_id"]
    38  		projectId := vars["project_id"]
    39  		ctx, err := model.LoadContext(taskId, buildId, versionId, patchId, projectId)
    40  		if err != nil {
    41  			// Some database lookup failed when fetching the data - log it
    42  			ra.LoggedError(w, r, http.StatusInternalServerError,
    43  				errors.Wrap(err, "Error loading project context"))
    44  			return
    45  		}
    46  		if ctx.ProjectRef != nil && ctx.ProjectRef.Private && GetUser(r) == nil {
    47  			http.Error(w, "Unauthorized", http.StatusUnauthorized)
    48  			return
    49  		}
    50  
    51  		if ctx.Patch != nil && GetUser(r) == nil {
    52  			http.Error(w, "Unauthorized", http.StatusUnauthorized)
    53  			return
    54  		}
    55  
    56  		context.Set(r, RestContext, &ctx)
    57  		next(w, r)
    58  	}
    59  }
    60  
    61  // GetRESTContext fetches the context associated with the request.
    62  func GetRESTContext(r *http.Request) (*model.Context, error) {
    63  	if rv := context.Get(r, RestContext); rv != nil {
    64  		return rv.(*model.Context), nil
    65  	}
    66  	return nil, errors.New("No context loaded")
    67  }
    68  
    69  // MustHaveRESTContext fetches the model.Context stored with the request, and panics if the key
    70  // is not set.
    71  func MustHaveRESTContext(r *http.Request) *model.Context {
    72  	pc, err := GetRESTContext(r)
    73  	if err != nil {
    74  		panic(err)
    75  	}
    76  	return pc
    77  }
    78  
    79  // AttachRESTHandler attaches a router at the given root that hooks up REST endpoint URIs to be
    80  // handled by the given restAPIService.
    81  func AttachRESTHandler(root *mux.Router, service restAPIService) http.Handler {
    82  	rtr := root.PathPrefix(fmt.Sprintf("/%s/v1/", evergreen.RestRoutePrefix)).Subrouter().StrictSlash(true)
    83  
    84  	// REST routes
    85  	rest := restAPI{service}
    86  
    87  	//restRouter := root.PathPrefix("/rest/v1/").Subrouter().StrictSlash(true)
    88  	rtr.HandleFunc("/projects", rest.loadCtx(rest.getProjectIds)).Name("project_list").Methods("GET")
    89  	rtr.HandleFunc("/projects/{project_id}", rest.loadCtx(rest.getProject)).Name("project_info").Methods("GET")
    90  	rtr.HandleFunc("/projects/{project_id}/versions", rest.loadCtx(rest.getRecentVersions)).Name("recent_versions").Methods("GET")
    91  	rtr.HandleFunc("/projects/{project_id}/revisions/{revision}", rest.loadCtx(rest.getVersionInfoViaRevision)).Name("version_info_via_revision").Methods("GET")
    92  	rtr.HandleFunc("/projects/{project_id}/test_history", rest.loadCtx(rest.GetTestHistory)).Name("test_history").Methods("GET")
    93  	rtr.HandleFunc("/projects/{project_id}/last_green", rest.loadCtx(rest.lastGreen)).Name("last_green_version").Methods("GET")
    94  	rtr.HandleFunc("/patches/{patch_id}", rest.loadCtx(rest.getPatch)).Name("patch_info").Methods("GET")
    95  	rtr.HandleFunc("/patches/{patch_id}/config", rest.loadCtx(rest.getPatchConfig)).Name("patch_config").Methods("GET")
    96  	rtr.HandleFunc("/versions/{version_id}", rest.loadCtx(rest.getVersionInfo)).Name("version_info").Methods("GET")
    97  	rtr.HandleFunc("/versions/{version_id}", requireUser(rest.loadCtx(rest.modifyVersionInfo), nil)).Name("").Methods("PATCH")
    98  	rtr.HandleFunc("/versions/{version_id}/status", rest.loadCtx(rest.getVersionStatus)).Name("version_status").Methods("GET")
    99  	rtr.HandleFunc("/versions/{version_id}/config", rest.loadCtx(rest.getVersionConfig)).Name("version_config").Methods("GET")
   100  	rtr.HandleFunc("/builds/{build_id}", rest.loadCtx(rest.getBuildInfo)).Name("build_info").Methods("GET")
   101  	rtr.HandleFunc("/builds/{build_id}/status", rest.loadCtx(rest.getBuildStatus)).Name("build_status").Methods("GET")
   102  	rtr.HandleFunc("/tasks/{task_id}", rest.loadCtx(rest.getTaskInfo)).Name("task_info").Methods("GET")
   103  	rtr.HandleFunc("/tasks/{task_id}/status", rest.loadCtx(rest.getTaskStatus)).Name("task_status").Methods("GET")
   104  	rtr.HandleFunc("/tasks/{task_name}/history", rest.loadCtx(rest.getTaskHistory)).Name("task_history").Methods("GET")
   105  	rtr.HandleFunc("/scheduler/host_utilization", rest.loadCtx(rest.getHostUtilizationStats)).Name("host_utilization").Methods("GET")
   106  	rtr.HandleFunc("/scheduler/distro/{distro_id}/stats", rest.loadCtx(rest.getAverageSchedulerStats)).Name("avg_stats").Methods("GET")
   107  	rtr.HandleFunc("/scheduler/makespans", rest.loadCtx(rest.getOptimalAndActualMakespans)).Name("makespan").Methods("GET")
   108  
   109  	return root
   110  
   111  }