221 lines
5.4 KiB
Go
221 lines
5.4 KiB
Go
package server
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"time"
|
|
|
|
"github.com/influxdata/chronograf"
|
|
)
|
|
|
|
type link struct {
|
|
Href string `json:"href"`
|
|
Rel string `json:"rel"`
|
|
}
|
|
|
|
type exploration struct {
|
|
Name string `json:"name"` // Exploration name given by user.
|
|
Data interface{} `json:"data"` // Serialization of the exploration config.
|
|
CreatedAt time.Time `json:"created_at"` // Time exploration was created
|
|
UpdatedAt time.Time `json:"updated_at"` // Latest time the exploration was updated.
|
|
Link link `json:"link"` // Self link
|
|
}
|
|
|
|
func newExploration(e *chronograf.Exploration) exploration {
|
|
rel := "self"
|
|
href := fmt.Sprintf("%s/%d/explorations/%d", "/chronograf/v1/users", e.UserID, e.ID)
|
|
return exploration{
|
|
Name: e.Name,
|
|
Data: e.Data,
|
|
CreatedAt: e.CreatedAt,
|
|
UpdatedAt: e.UpdatedAt,
|
|
Link: link{
|
|
Rel: rel,
|
|
Href: href,
|
|
},
|
|
}
|
|
}
|
|
|
|
type explorations struct {
|
|
Explorations []exploration `json:"explorations"`
|
|
}
|
|
|
|
// Explorations returns all explorations scoped by user id.
|
|
func (h *Service) Explorations(w http.ResponseWriter, r *http.Request) {
|
|
id, err := paramID("id", r)
|
|
if err != nil {
|
|
Error(w, http.StatusUnprocessableEntity, err.Error(), h.Logger)
|
|
return
|
|
}
|
|
|
|
ctx := r.Context()
|
|
mrExs, err := h.ExplorationStore.Query(ctx, chronograf.UserID(id))
|
|
if err != nil {
|
|
unknownErrorWithMessage(w, err, h.Logger)
|
|
return
|
|
}
|
|
|
|
exs := make([]exploration, len(mrExs))
|
|
for i, e := range mrExs {
|
|
exs[i] = newExploration(e)
|
|
}
|
|
res := explorations{
|
|
Explorations: exs,
|
|
}
|
|
|
|
encodeJSON(w, http.StatusOK, res, h.Logger)
|
|
}
|
|
|
|
// ExplorationsID retrieves exploration ID scoped under user.
|
|
func (h *Service) ExplorationsID(w http.ResponseWriter, r *http.Request) {
|
|
eID, err := paramID("eid", r)
|
|
if err != nil {
|
|
Error(w, http.StatusUnprocessableEntity, err.Error(), h.Logger)
|
|
return
|
|
}
|
|
|
|
uID, err := paramID("id", r)
|
|
if err != nil {
|
|
Error(w, http.StatusUnprocessableEntity, err.Error(), h.Logger)
|
|
return
|
|
}
|
|
|
|
ctx := r.Context()
|
|
e, err := h.ExplorationStore.Get(ctx, chronograf.ExplorationID(eID))
|
|
if err != nil || e.UserID != chronograf.UserID(uID) {
|
|
notFound(w, eID, h.Logger)
|
|
return
|
|
}
|
|
|
|
res := newExploration(e)
|
|
encodeJSON(w, http.StatusOK, res, h.Logger)
|
|
}
|
|
|
|
type patchExplorationRequest struct {
|
|
Data interface{} `json:"data,omitempty"` // Serialized configuration
|
|
Name *string `json:"name,omitempty"` // Exploration name given by user.
|
|
}
|
|
|
|
// UpdateExploration incrementally updates exploration
|
|
func (h *Service) UpdateExploration(w http.ResponseWriter, r *http.Request) {
|
|
id, err := paramID("eid", r)
|
|
if err != nil {
|
|
Error(w, http.StatusUnprocessableEntity, err.Error(), h.Logger)
|
|
return
|
|
}
|
|
|
|
uID, err := paramID("id", r)
|
|
if err != nil {
|
|
Error(w, http.StatusUnprocessableEntity, err.Error(), h.Logger)
|
|
return
|
|
}
|
|
|
|
ctx := r.Context()
|
|
e, err := h.ExplorationStore.Get(ctx, chronograf.ExplorationID(id))
|
|
if err != nil || e.UserID != chronograf.UserID(uID) {
|
|
notFound(w, id, h.Logger)
|
|
return
|
|
}
|
|
|
|
var req patchExplorationRequest
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
invalidJSON(w, h.Logger)
|
|
return
|
|
}
|
|
|
|
if req.Data != nil {
|
|
var ok bool
|
|
if e.Data, ok = req.Data.(string); !ok {
|
|
err := fmt.Errorf("Error: Exploration data is not a string")
|
|
invalidData(w, err, h.Logger)
|
|
return
|
|
}
|
|
}
|
|
|
|
if req.Name != nil {
|
|
e.Name = *req.Name
|
|
}
|
|
|
|
if err := h.ExplorationStore.Update(ctx, e); err != nil {
|
|
msg := "Error: Failed to update Exploration"
|
|
Error(w, http.StatusInternalServerError, msg, h.Logger)
|
|
return
|
|
}
|
|
|
|
res := newExploration(e)
|
|
encodeJSON(w, http.StatusOK, res, h.Logger)
|
|
}
|
|
|
|
type postExplorationRequest struct {
|
|
Data interface{} `json:"data"` // Serialization of config.
|
|
Name string `json:"name,omitempty"` // Exploration name given by user.
|
|
}
|
|
|
|
// NewExploration adds valid exploration scoped by user id.
|
|
func (h *Service) NewExploration(w http.ResponseWriter, r *http.Request) {
|
|
uID, err := paramID("id", r)
|
|
if err != nil {
|
|
Error(w, http.StatusUnprocessableEntity, err.Error(), h.Logger)
|
|
return
|
|
}
|
|
|
|
// TODO: Check user if user exists.
|
|
var req postExplorationRequest
|
|
if err = json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
invalidJSON(w, h.Logger)
|
|
return
|
|
}
|
|
|
|
data := ""
|
|
if req.Data != nil {
|
|
data, _ = req.Data.(string)
|
|
}
|
|
|
|
e := &chronograf.Exploration{
|
|
Name: req.Name,
|
|
UserID: chronograf.UserID(uID),
|
|
Data: data,
|
|
}
|
|
|
|
ctx := r.Context()
|
|
e, err = h.ExplorationStore.Add(ctx, e)
|
|
if err != nil {
|
|
msg := fmt.Errorf("Error: Failed to save Exploration")
|
|
unknownErrorWithMessage(w, msg, h.Logger)
|
|
return
|
|
}
|
|
|
|
res := newExploration(e)
|
|
w.Header().Add("Location", res.Link.Href)
|
|
encodeJSON(w, http.StatusCreated, res, h.Logger)
|
|
}
|
|
|
|
// RemoveExploration deletes exploration from store.
|
|
func (h *Service) RemoveExploration(w http.ResponseWriter, r *http.Request) {
|
|
eID, err := paramID("eid", r)
|
|
if err != nil {
|
|
Error(w, http.StatusUnprocessableEntity, err.Error(), h.Logger)
|
|
return
|
|
}
|
|
|
|
uID, err := paramID("id", r)
|
|
if err != nil {
|
|
Error(w, http.StatusUnprocessableEntity, err.Error(), h.Logger)
|
|
return
|
|
}
|
|
|
|
ctx := r.Context()
|
|
e, err := h.ExplorationStore.Get(ctx, chronograf.ExplorationID(eID))
|
|
if err != nil || e.UserID != chronograf.UserID(uID) {
|
|
notFound(w, eID, h.Logger)
|
|
return
|
|
}
|
|
|
|
if err := h.ExplorationStore.Delete(ctx, &chronograf.Exploration{ID: chronograf.ExplorationID(eID)}); err != nil {
|
|
unknownErrorWithMessage(w, err, h.Logger)
|
|
return
|
|
}
|
|
w.WriteHeader(http.StatusNoContent)
|
|
}
|