influxdb/server/templates.go

249 lines
6.3 KiB
Go

package server
import (
"encoding/json"
"fmt"
"net/http"
"github.com/bouk/httprouter"
"github.com/influxdata/chronograf"
"github.com/influxdata/chronograf/uuid"
)
// ValidTemplateRequest checks if the request sent to the server is the correct format.
func ValidTemplateRequest(template *chronograf.Template) error {
switch template.Type {
default:
return fmt.Errorf("Unknown template type %s", template.Type)
case "query", "constant", "csv", "fieldKeys", "tagKeys", "tagValues", "measurements", "databases":
}
for _, v := range template.Values {
switch v.Type {
default:
return fmt.Errorf("Unknown template variable type %s", v.Type)
case "csv", "fieldKey", "tagKey", "tagValue", "measurement", "database", "constant":
}
}
if template.Type == "query" && template.Query == nil {
return fmt.Errorf("No query set for template of type 'query'")
}
return nil
}
type templateLinks struct {
Self string `json:"self"` // Self link mapping to this resource
}
type templateResponse struct {
chronograf.Template
Links templateLinks `json:"links"`
}
func newTemplateResponses(dID chronograf.DashboardID, tmps []chronograf.Template) []templateResponse {
res := make([]templateResponse, len(tmps))
for i, t := range tmps {
res[i] = newTemplateResponse(dID, t)
}
return res
}
type templatesResponses struct {
Templates []templateResponse `json:"templates"`
}
func newTemplateResponse(dID chronograf.DashboardID, tmp chronograf.Template) templateResponse {
base := "/chronograf/v1/dashboards"
return templateResponse{
Template: tmp,
Links: templateLinks{
Self: fmt.Sprintf("%s/%d/templates/%s", base, dID, tmp.ID),
},
}
}
// Templates returns all templates from a dashboard within the store
func (s *Service) Templates(w http.ResponseWriter, r *http.Request) {
id, err := paramID("id", r)
if err != nil {
Error(w, http.StatusUnprocessableEntity, err.Error(), s.Logger)
return
}
ctx := r.Context()
d, err := s.DashboardsStore.Get(ctx, chronograf.DashboardID(id))
if err != nil {
notFound(w, id, s.Logger)
return
}
res := templatesResponses{
Templates: newTemplateResponses(chronograf.DashboardID(id), d.Templates),
}
encodeJSON(w, http.StatusOK, res, s.Logger)
}
// NewTemplate adds a template to an existing dashboard
func (s *Service) NewTemplate(w http.ResponseWriter, r *http.Request) {
id, err := paramID("id", r)
if err != nil {
Error(w, http.StatusUnprocessableEntity, err.Error(), s.Logger)
return
}
ctx := r.Context()
dash, err := s.DashboardsStore.Get(ctx, chronograf.DashboardID(id))
if err != nil {
notFound(w, id, s.Logger)
return
}
var template chronograf.Template
if err := json.NewDecoder(r.Body).Decode(&template); err != nil {
invalidJSON(w, s.Logger)
return
}
if err := ValidTemplateRequest(&template); err != nil {
invalidData(w, err, s.Logger)
return
}
ids := uuid.V4{}
tid, err := ids.Generate()
if err != nil {
msg := fmt.Sprintf("Error creating template ID for dashboard %d: %v", id, err)
Error(w, http.StatusInternalServerError, msg, s.Logger)
return
}
template.ID = chronograf.TemplateID(tid)
dash.Templates = append(dash.Templates, template)
if err := s.DashboardsStore.Update(ctx, dash); err != nil {
msg := fmt.Sprintf("Error adding template %s to dashboard %d: %v", tid, id, err)
Error(w, http.StatusInternalServerError, msg, s.Logger)
return
}
res := newTemplateResponse(dash.ID, template)
encodeJSON(w, http.StatusOK, res, s.Logger)
}
// TemplateID retrieves a specific template from a dashboard
func (s *Service) TemplateID(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
id, err := paramID("id", r)
if err != nil {
Error(w, http.StatusUnprocessableEntity, err.Error(), s.Logger)
return
}
dash, err := s.DashboardsStore.Get(ctx, chronograf.DashboardID(id))
if err != nil {
notFound(w, id, s.Logger)
return
}
tid := httprouter.GetParamFromContext(ctx, "tid")
for _, t := range dash.Templates {
if t.ID == chronograf.TemplateID(tid) {
res := newTemplateResponse(chronograf.DashboardID(id), t)
encodeJSON(w, http.StatusOK, res, s.Logger)
return
}
}
notFound(w, id, s.Logger)
}
// RemoveTemplate removes a specific template from an existing dashboard
func (s *Service) RemoveTemplate(w http.ResponseWriter, r *http.Request) {
id, err := paramID("id", r)
if err != nil {
Error(w, http.StatusUnprocessableEntity, err.Error(), s.Logger)
return
}
ctx := r.Context()
dash, err := s.DashboardsStore.Get(ctx, chronograf.DashboardID(id))
if err != nil {
notFound(w, id, s.Logger)
return
}
tid := httprouter.GetParamFromContext(ctx, "tid")
pos := -1
for i, t := range dash.Templates {
if t.ID == chronograf.TemplateID(tid) {
pos = i
break
}
}
if pos == -1 {
notFound(w, id, s.Logger)
return
}
dash.Templates = append(dash.Templates[:pos], dash.Templates[pos+1:]...)
if err := s.DashboardsStore.Update(ctx, dash); err != nil {
msg := fmt.Sprintf("Error removing template %s from dashboard %d: %v", tid, id, err)
Error(w, http.StatusInternalServerError, msg, s.Logger)
return
}
w.WriteHeader(http.StatusNoContent)
}
// ReplaceTemplate replaces a template entirely within an existing dashboard
func (s *Service) ReplaceTemplate(w http.ResponseWriter, r *http.Request) {
id, err := paramID("id", r)
if err != nil {
Error(w, http.StatusUnprocessableEntity, err.Error(), s.Logger)
return
}
ctx := r.Context()
dash, err := s.DashboardsStore.Get(ctx, chronograf.DashboardID(id))
if err != nil {
notFound(w, id, s.Logger)
return
}
tid := httprouter.GetParamFromContext(ctx, "tid")
pos := -1
for i, t := range dash.Templates {
if t.ID == chronograf.TemplateID(tid) {
pos = i
break
}
}
if pos == -1 {
notFound(w, id, s.Logger)
return
}
var template chronograf.Template
if err := json.NewDecoder(r.Body).Decode(&template); err != nil {
invalidJSON(w, s.Logger)
return
}
if err := ValidTemplateRequest(&template); err != nil {
invalidData(w, err, s.Logger)
return
}
template.ID = chronograf.TemplateID(tid)
dash.Templates[pos] = template
if err := s.DashboardsStore.Update(ctx, dash); err != nil {
msg := fmt.Sprintf("Error updating template %s in dashboard %d: %v", tid, id, err)
Error(w, http.StatusInternalServerError, msg, s.Logger)
return
}
res := newTemplateResponse(chronograf.DashboardID(id), template)
encodeJSON(w, http.StatusOK, res, s.Logger)
}