influxdb/server/users.go

204 lines
4.6 KiB
Go

package server
import (
"encoding/json"
"fmt"
"net/http"
"sort"
"github.com/bouk/httprouter"
"github.com/influxdata/chronograf"
)
type userRequest struct {
ID uint64 `json:"id,string"`
Name string `json:"name"`
Provider string `json:"provider"`
Scheme string `json:"scheme"`
}
func (r *userRequest) ValidCreate() error {
if r.Name == "" {
return fmt.Errorf("Name required on Chronograf User request body")
}
if r.Provider == "" {
return fmt.Errorf("Provider required on Chronograf User request body")
}
if r.Scheme == "" {
return fmt.Errorf("Scheme required on Chronograf User request body")
}
return nil
}
// TODO: Provide detailed error message
// TODO: Reconsider what fields should actually be updateable once this is more robust
func (r *userRequest) ValidUpdate() error {
if r.Name == "" && r.Provider == "" && r.Scheme == "" {
return fmt.Errorf("No fields to update")
}
return nil
}
type userResponse struct {
Links selfLinks `json:"links"`
ID uint64 `json:"id,string"`
Name string `json:"name"`
Provider string `json:"provider"`
Scheme string `json:"scheme"`
}
func newUserResponse(u *chronograf.User) *userResponse {
return &userResponse{
ID: u.ID,
Name: u.Name,
Provider: u.Provider,
Scheme: u.Scheme,
Links: selfLinks{
Self: fmt.Sprintf("/chronograf/v1/users/%d", u.ID),
},
}
}
type usersResponse struct {
Links selfLinks `json:"links"`
Users []*userResponse `json:"users"`
}
func newUsersResponse(users []chronograf.User) *usersResponse {
usersResp := make([]*userResponse, len(users))
for i, user := range users {
usersResp[i] = newUserResponse(&user)
}
sort.Slice(usersResp, func(i, j int) bool {
return usersResp[i].ID < usersResp[j].ID
})
return &usersResponse{
Users: usersResp,
Links: selfLinks{
Self: "/chronograf/v1/users",
},
}
}
// UserID retrieves a Chronograf user with ID from store
func (s *Service) UserID(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
id := httprouter.GetParamFromContext(ctx, "id")
user, err := s.UsersStore.Get(ctx, id)
if err != nil {
Error(w, http.StatusBadRequest, err.Error(), s.Logger)
return
}
res := newUserResponse(user)
encodeJSON(w, http.StatusOK, res, s.Logger)
}
// NewUser adds a new Chronograf user to store
func (s *Service) NewUser(w http.ResponseWriter, r *http.Request) {
var req userRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
invalidJSON(w, s.Logger)
return
}
if err := req.ValidCreate(); err != nil {
invalidData(w, err, s.Logger)
return
}
ctx := r.Context()
user := &chronograf.User{
Name: req.Name,
Provider: req.Provider,
Scheme: req.Scheme,
}
res, err := s.UsersStore.Add(ctx, user)
if err != nil {
Error(w, http.StatusBadRequest, err.Error(), s.Logger)
return
}
cu := newUserResponse(res)
location(w, cu.Links.Self)
encodeJSON(w, http.StatusCreated, cu, s.Logger)
}
// RemoveUser deletes a Chronograf user from store
func (s *Service) RemoveUser(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
id := httprouter.GetParamFromContext(ctx, "id")
u, err := s.UsersStore.Get(ctx, id)
if err != nil {
Error(w, http.StatusNotFound, err.Error(), s.Logger)
}
if err := s.UsersStore.Delete(ctx, u); err != nil {
Error(w, http.StatusBadRequest, err.Error(), s.Logger)
}
w.WriteHeader(http.StatusNoContent)
}
// UpdateUser updates a Chronograf user in store
func (s *Service) UpdateUser(w http.ResponseWriter, r *http.Request) {
var req userRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
invalidJSON(w, s.Logger)
return
}
if err := req.ValidUpdate(); err != nil {
invalidData(w, err, s.Logger)
return
}
ctx := r.Context()
id := httprouter.GetParamFromContext(ctx, "id")
u, err := s.UsersStore.Get(ctx, id)
if err != nil {
Error(w, http.StatusNotFound, err.Error(), s.Logger)
}
if req.Name != "" {
u.Name = req.Name
}
if req.Provider != "" {
u.Provider = req.Provider
}
if req.Scheme != "" {
u.Scheme = req.Scheme
}
err = s.UsersStore.Update(ctx, u)
if err != nil {
Error(w, http.StatusBadRequest, err.Error(), s.Logger)
return
}
cu := newUserResponse(u)
location(w, cu.Links.Self)
encodeJSON(w, http.StatusOK, cu, s.Logger)
}
// Users retrieves all Chronograf users from store
func (s *Service) Users(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
users, err := s.UsersStore.All(ctx)
if err != nil {
Error(w, http.StatusBadRequest, err.Error(), s.Logger)
return
}
res := newUsersResponse(users)
encodeJSON(w, http.StatusOK, res, s.Logger)
}
func location(w http.ResponseWriter, self string) {
w.Header().Add("Location", self)
}