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 }