Browse Source

refactorin api code for user routes, preparation for admin improvements, #2014

Torkel Ödegaard 10 years ago
parent
commit
5270c4bc74

+ 9 - 3
pkg/api/api.go

@@ -55,15 +55,21 @@ func Register(r *macaron.Macaron) {
 	r.Group("/api", func() {
 		// user
 		r.Group("/user", func() {
-			r.Get("/", GetUser)
+			r.Get("/", wrap(GetSignedInUser))
 			r.Put("/", bind(m.UpdateUserCommand{}), UpdateUser)
 			r.Post("/using/:id", UserSetUsingOrg)
-			r.Get("/orgs", GetUserOrgList)
+			r.Get("/orgs", wrap(GetSignedInUserOrgList))
 			r.Post("/stars/dashboard/:id", StarDashboard)
 			r.Delete("/stars/dashboard/:id", UnstarDashboard)
 			r.Put("/password", bind(m.ChangeUserPasswordCommand{}), ChangeUserPassword)
 		})
 
+		// users
+		r.Group("/users", func() {
+			r.Get("/:id/", wrap(GetUserById))
+			r.Get("/:id/org", wrap(GetUserOrgList))
+		}, reqGrafanaAdmin)
+
 		// account
 		r.Group("/org", func() {
 			r.Get("/", GetOrg)
@@ -127,5 +133,5 @@ func Register(r *macaron.Macaron) {
 	// rendering
 	r.Get("/render/*", reqSignedIn, RenderToPng)
 
-	r.NotFound(NotFound)
+	r.NotFound(NotFoundHandler)
 }

+ 111 - 0
pkg/api/common.go

@@ -0,0 +1,111 @@
+package api
+
+import (
+	"encoding/json"
+	"net/http"
+
+	"github.com/Unknwon/macaron"
+	"github.com/grafana/grafana/pkg/log"
+	"github.com/grafana/grafana/pkg/metrics"
+	"github.com/grafana/grafana/pkg/middleware"
+	"github.com/grafana/grafana/pkg/setting"
+)
+
+var (
+	NotFound    = ApiError(404, "Not found", nil)
+	ServerError = ApiError(500, "Server error", nil)
+)
+
+type Response interface {
+	WriteTo(out http.ResponseWriter)
+}
+
+type NormalResponse struct {
+	status int
+	body   []byte
+	header http.Header
+}
+
+func wrap(action func(c *middleware.Context) Response) macaron.Handler {
+	return func(c *middleware.Context) {
+		res := action(c)
+		if res == nil {
+			res = ServerError
+		}
+		res.WriteTo(c.Resp)
+	}
+}
+
+func (r *NormalResponse) WriteTo(out http.ResponseWriter) {
+	header := out.Header()
+	for k, v := range r.header {
+		header[k] = v
+	}
+	out.WriteHeader(r.status)
+	out.Write(r.body)
+}
+
+func (r *NormalResponse) Cache(ttl string) *NormalResponse {
+	return r.Header("Cache-Control", "public,max-age="+ttl)
+}
+
+func (r *NormalResponse) Header(key, value string) *NormalResponse {
+	r.header.Set(key, value)
+	return r
+}
+
+// functions to create responses
+
+func Empty(status int) *NormalResponse {
+	return Respond(status, nil)
+}
+
+func Json(status int, body interface{}) *NormalResponse {
+	return Respond(status, body).Header("Content-Type", "application/json")
+}
+
+func ApiError(status int, message string, err error) *NormalResponse {
+	resp := make(map[string]interface{})
+
+	if err != nil {
+		log.Error(4, "%s: %v", message, err)
+		if setting.Env != setting.PROD {
+			resp["error"] = err.Error()
+		}
+	}
+
+	switch status {
+	case 404:
+		resp["message"] = "Not Found"
+		metrics.M_Api_Status_500.Inc(1)
+	case 500:
+		metrics.M_Api_Status_404.Inc(1)
+		resp["message"] = "Internal Server Error"
+	}
+
+	if message != "" {
+		resp["message"] = message
+	}
+
+	return Json(status, resp)
+}
+
+func Respond(status int, body interface{}) *NormalResponse {
+	var b []byte
+	var err error
+	switch t := body.(type) {
+	case []byte:
+		b = t
+	case string:
+		b = []byte(t)
+	default:
+		if b, err = json.Marshal(body); err != nil {
+			return ApiError(500, "body json marshal", err)
+		}
+	}
+	return &NormalResponse{
+		body:   b,
+		status: status,
+		header: make(http.Header),
+	}
+}

+ 1 - 1
pkg/api/index.go

@@ -59,7 +59,7 @@ func Index(c *middleware.Context) {
 	c.HTML(200, "index")
 }
 
-func NotFound(c *middleware.Context) {
+func NotFoundHandler(c *middleware.Context) {
 	if c.IsApiRequest() {
 		c.JsonApiErr(404, "Not found", nil)
 		return

+ 28 - 17
pkg/api/user.go

@@ -7,15 +7,24 @@ import (
 	"github.com/grafana/grafana/pkg/util"
 )
 
-func GetUser(c *middleware.Context) {
-	query := m.GetUserProfileQuery{UserId: c.UserId}
+// GET /api/user  (current authenticated user)
+func GetSignedInUser(c *middleware.Context) Response {
+	return getUserUserProfile(c.UserId)
+}
+
+// GET /api/user/:id
+func GetUserById(c *middleware.Context) Response {
+	return getUserUserProfile(c.ParamsInt64(":id"))
+}
+
+func getUserUserProfile(userId int64) Response {
+	query := m.GetUserProfileQuery{UserId: userId}
 
 	if err := bus.Dispatch(&query); err != nil {
-		c.JsonApiErr(500, "Failed to get user", err)
-		return
+		return ApiError(500, "Failed to get user", err)
 	}
 
-	c.JSON(200, query.Result)
+	return Json(200, query.Result)
 }
 
 func UpdateUser(c *middleware.Context, cmd m.UpdateUserCommand) {
@@ -29,22 +38,24 @@ func UpdateUser(c *middleware.Context, cmd m.UpdateUserCommand) {
 	c.JsonOK("User updated")
 }
 
-func GetUserOrgList(c *middleware.Context) {
-	query := m.GetUserOrgListQuery{UserId: c.UserId}
+// GET /api/user/orgs
+func GetSignedInUserOrgList(c *middleware.Context) Response {
+	return getUserOrgList(c.UserId)
+}
 
-	if err := bus.Dispatch(&query); err != nil {
-		c.JsonApiErr(500, "Failed to get user organizations", err)
-		return
-	}
+// GET /api/user/:id/orgs
+func GetUserOrgList(c *middleware.Context) Response {
+	return getUserOrgList(c.ParamsInt64(":id"))
+}
 
-	for _, ac := range query.Result {
-		if ac.OrgId == c.OrgId {
-			ac.IsUsing = true
-			break
-		}
+func getUserOrgList(userId int64) Response {
+	query := m.GetUserOrgListQuery{UserId: userId}
+
+	if err := bus.Dispatch(&query); err != nil {
+		return ApiError(500, "Faile to get user organziations", err)
 	}
 
-	c.JSON(200, query.Result)
+	return Json(200, query.Result)
 }
 
 func validateUsingOrg(userId int64, orgId int64) bool {

+ 3 - 4
pkg/models/org.go

@@ -58,8 +58,7 @@ type OrgDTO struct {
 }
 
 type UserOrgDTO struct {
-	OrgId   int64    `json:"orgId"`
-	Name    string   `json:"name"`
-	Role    RoleType `json:"role"`
-	IsUsing bool     `json:"isUsing"`
+	OrgId int64    `json:"orgId"`
+	Name  string   `json:"name"`
+	Role  RoleType `json:"role"`
 }

+ 2 - 2
public/app/features/admin/partials/edit_user.html

@@ -25,7 +25,7 @@
 					</ul>
 					<div class="clearfix"></div>
 				</div>
-				<div class="tight-form" style="margin-top: 5px">
+				<div class="tight-form">
 					<ul class="tight-form-list">
 						<li class="tight-form-item" style="width: 100px">
 							<strong>Email</strong>
@@ -36,7 +36,7 @@
 					</ul>
 					<div class="clearfix"></div>
 				</div>
-				<div class="tight-form" style="margin-top: 5px">
+				<div class="tight-form">
 					<ul class="tight-form-list">
 						<li class="tight-form-item" style="width: 100px">
 							<strong>Username</strong>

+ 3 - 3
public/app/features/admin/partials/new_user.html

@@ -24,7 +24,7 @@
 					</ul>
 					<div class="clearfix"></div>
 				</div>
-				<div class="tight-form" style="margin-top: 5px">
+				<div class="tight-form">
 					<ul class="tight-form-list">
 						<li class="tight-form-item" style="width: 100px">
 							<strong>Email</strong>
@@ -35,7 +35,7 @@
 					</ul>
 					<div class="clearfix"></div>
 				</div>
-				<div class="tight-form" style="margin-top: 5px">
+				<div class="tight-form">
 					<ul class="tight-form-list">
 						<li class="tight-form-item" style="width: 100px">
 							<strong>Username</strong>
@@ -46,7 +46,7 @@
 					</ul>
 					<div class="clearfix"></div>
 				</div>
-				<div class="tight-form" style="margin-top: 5px">
+				<div class="tight-form">
 					<ul class="tight-form-list">
 						<li class="tight-form-item" style="width: 100px">
 							<strong>Password</strong>

+ 2 - 2
public/app/features/profile/partials/profile.html

@@ -71,10 +71,10 @@
 				<td style="width: 98%"><strong>Name: </strong> {{org.name}}</td>
 				<td><strong>Role: </strong> {{org.role}}</td>
 				<td class="nobg max-width-btns">
-					<span class="btn btn-primary btn-mini" ng-show="org.isUsing">
+					<span class="btn btn-primary btn-mini" ng-show="org.orgId === contextSrv.user.orgId">
 						Current
 					</span>
-					<a ng-click="setUsingOrg(org)" class="btn btn-inverse btn-mini" ng-show="!org.isUsing">
+					<a ng-click="setUsingOrg(org)" class="btn btn-inverse btn-mini" ng-show="org.orgId !== contextSrv.user.orgId">
 						Select
 					</a>
 				</td>