influxdb/http/view_service.go

361 lines
8.9 KiB
Go

// NOTE: This service has been deprecated and should not be used.
// Views are now resources that belong to dashboards. The reason for
// this is due to how we authorize operations against views.
package http
import (
"context"
"encoding/json"
"fmt"
"net/http"
"github.com/influxdata/platform"
"github.com/julienschmidt/httprouter"
"go.uber.org/zap"
)
// ViewHandler is the handler for the view service
type ViewHandler struct {
*httprouter.Router
Logger *zap.Logger
ViewService platform.ViewService
UserResourceMappingService platform.UserResourceMappingService
LabelService platform.LabelService
UserService platform.UserService
}
const (
viewsPath = "/api/v2/views"
viewsIDPath = "/api/v2/views/:id"
viewsIDMembersPath = "/api/v2/views/:id/members"
viewsIDMembersIDPath = "/api/v2/views/:id/members/:userID"
viewsIDOwnersPath = "/api/v2/views/:id/owners"
viewsIDOwnersIDPath = "/api/v2/views/:id/owners/:userID"
viewsIDLabelsPath = "/api/v2/views/:id/labels"
viewsIDLabelsNamePath = "/api/v2/views/:id/labels/:name"
)
// NewViewHandler returns a new instance of ViewHandler.
func NewViewHandler(mappingService platform.UserResourceMappingService, labelService platform.LabelService, userService platform.UserService) *ViewHandler {
h := &ViewHandler{
Router: NewRouter(),
Logger: zap.NewNop(),
UserResourceMappingService: mappingService,
LabelService: labelService,
UserService: userService,
}
h.HandlerFunc("POST", viewsPath, h.handlePostViews)
h.HandlerFunc("GET", viewsPath, h.handleGetViews)
h.HandlerFunc("GET", viewsIDPath, h.handleGetView)
h.HandlerFunc("DELETE", viewsIDPath, h.handleDeleteView)
h.HandlerFunc("PATCH", viewsIDPath, h.handlePatchView)
h.HandlerFunc("POST", viewsIDMembersPath, newPostMemberHandler(h.UserResourceMappingService, h.UserService, platform.DashboardsResource, platform.Member))
h.HandlerFunc("GET", viewsIDMembersPath, newGetMembersHandler(h.UserResourceMappingService, h.UserService, platform.DashboardsResource, platform.Member))
h.HandlerFunc("DELETE", viewsIDMembersIDPath, newDeleteMemberHandler(h.UserResourceMappingService, platform.Member))
h.HandlerFunc("POST", viewsIDOwnersPath, newPostMemberHandler(h.UserResourceMappingService, h.UserService, platform.DashboardsResource, platform.Owner))
h.HandlerFunc("GET", viewsIDOwnersPath, newGetMembersHandler(h.UserResourceMappingService, h.UserService, platform.DashboardsResource, platform.Owner))
h.HandlerFunc("DELETE", viewsIDOwnersIDPath, newDeleteMemberHandler(h.UserResourceMappingService, platform.Owner))
h.HandlerFunc("GET", viewsIDLabelsPath, newGetLabelsHandler(h.LabelService))
h.HandlerFunc("POST", viewsIDLabelsPath, newPostLabelHandler(h.LabelService))
h.HandlerFunc("DELETE", viewsIDLabelsNamePath, newDeleteLabelHandler(h.LabelService))
h.HandlerFunc("PATCH", viewsIDLabelsNamePath, newPatchLabelHandler(h.LabelService))
return h
}
type viewLinks struct {
Self string `json:"self"`
}
type viewResponse struct {
platform.View
Links viewLinks `json:"links"`
}
func (r viewResponse) MarshalJSON() ([]byte, error) {
props, err := platform.MarshalViewPropertiesJSON(r.Properties)
if err != nil {
return nil, err
}
return json.Marshal(struct {
platform.ViewContents
Links viewLinks `json:"links"`
Properties json.RawMessage `json:"properties"`
}{
ViewContents: r.ViewContents,
Links: r.Links,
Properties: props,
})
}
func newViewResponse(c *platform.View) viewResponse {
return viewResponse{
Links: viewLinks{
Self: fmt.Sprintf("/api/v2/views/%s", c.ID),
},
View: *c,
}
}
// handleGetViews returns all views within the store.
func (h *ViewHandler) handleGetViews(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
req := decodeGetViewsRequest(ctx, r)
views, _, err := h.ViewService.FindViews(ctx, req.filter)
if err != nil {
EncodeError(ctx, err, w)
return
}
if err := encodeResponse(ctx, w, http.StatusOK, newGetViewsResponse(views)); err != nil {
logEncodingError(h.Logger, r, err)
return
}
}
type getViewsRequest struct {
filter platform.ViewFilter
}
func decodeGetViewsRequest(ctx context.Context, r *http.Request) *getViewsRequest {
qp := r.URL.Query()
return &getViewsRequest{
filter: platform.ViewFilter{
Types: qp["type"],
},
}
}
type getViewsLinks struct {
Self string `json:"self"`
}
type getViewsResponse struct {
Links getViewsLinks `json:"links"`
Views []viewResponse `json:"views"`
}
func newGetViewsResponse(views []*platform.View) getViewsResponse {
res := getViewsResponse{
Links: getViewsLinks{
Self: "/api/v2/views",
},
Views: make([]viewResponse, 0, len(views)),
}
for _, view := range views {
res.Views = append(res.Views, newViewResponse(view))
}
return res
}
// handlePostViews creates a new view.
func (h *ViewHandler) handlePostViews(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
req, err := decodePostViewRequest(ctx, r)
if err != nil {
EncodeError(ctx, err, w)
return
}
if err := h.ViewService.CreateView(ctx, req.View); err != nil {
EncodeError(ctx, err, w)
return
}
if err := encodeResponse(ctx, w, http.StatusCreated, newViewResponse(req.View)); err != nil {
logEncodingError(h.Logger, r, err)
return
}
}
type postViewRequest struct {
View *platform.View
}
func decodePostViewRequest(ctx context.Context, r *http.Request) (*postViewRequest, error) {
c := &platform.View{}
if err := json.NewDecoder(r.Body).Decode(c); err != nil {
return nil, err
}
return &postViewRequest{
View: c,
}, nil
}
// hanldeGetView retrieves a view by ID.
func (h *ViewHandler) handleGetView(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
req, err := decodeGetViewRequest(ctx, r)
if err != nil {
EncodeError(ctx, err, w)
return
}
view, err := h.ViewService.FindViewByID(ctx, req.ViewID)
if err != nil {
EncodeError(ctx, err, w)
return
}
if err := encodeResponse(ctx, w, http.StatusOK, newViewResponse(view)); err != nil {
logEncodingError(h.Logger, r, err)
return
}
}
type getViewRequest struct {
ViewID platform.ID
}
func decodeGetViewRequest(ctx context.Context, r *http.Request) (*getViewRequest, error) {
params := httprouter.ParamsFromContext(ctx)
id := params.ByName("id")
if id == "" {
return nil, &platform.Error{
Code: platform.EInvalid,
Msg: "url missing id",
}
}
var i platform.ID
if err := i.DecodeFromString(id); err != nil {
return nil, err
}
return &getViewRequest{
ViewID: i,
}, nil
}
// handleDeleteView removes a view by ID.
func (h *ViewHandler) handleDeleteView(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
req, err := decodeDeleteViewRequest(ctx, r)
if err != nil {
EncodeError(ctx, err, w)
return
}
if err := h.ViewService.DeleteView(ctx, req.ViewID); err != nil {
EncodeError(ctx, err, w)
return
}
w.WriteHeader(http.StatusNoContent)
}
type deleteViewRequest struct {
ViewID platform.ID
}
func decodeDeleteViewRequest(ctx context.Context, r *http.Request) (*deleteViewRequest, error) {
params := httprouter.ParamsFromContext(ctx)
id := params.ByName("id")
if id == "" {
return nil, &platform.Error{
Code: platform.EInvalid,
Msg: "url missing id",
}
}
var i platform.ID
if err := i.DecodeFromString(id); err != nil {
return nil, err
}
return &deleteViewRequest{
ViewID: i,
}, nil
}
// handlePatchView updates a view.
func (h *ViewHandler) handlePatchView(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
req, pe := decodePatchViewRequest(ctx, r)
if pe != nil {
EncodeError(ctx, pe, w)
return
}
view, err := h.ViewService.UpdateView(ctx, req.ViewID, req.Upd)
if err != nil {
EncodeError(ctx, err, w)
return
}
if err := encodeResponse(ctx, w, http.StatusOK, newViewResponse(view)); err != nil {
logEncodingError(h.Logger, r, err)
return
}
}
type patchViewRequest struct {
ViewID platform.ID
Upd platform.ViewUpdate
}
func decodePatchViewRequest(ctx context.Context, r *http.Request) (*patchViewRequest, *platform.Error) {
req := &patchViewRequest{}
upd := platform.ViewUpdate{}
if err := json.NewDecoder(r.Body).Decode(&upd); err != nil {
return nil, &platform.Error{
Code: platform.EInvalid,
Err: err,
}
}
req.Upd = upd
params := httprouter.ParamsFromContext(ctx)
id := params.ByName("id")
if id == "" {
return nil, &platform.Error{
Code: platform.EInvalid,
Msg: "url missing id",
}
}
var i platform.ID
if err := i.DecodeFromString(id); err != nil {
return nil, &platform.Error{
Err: err,
}
}
req.ViewID = i
if err := req.Valid(); err != nil {
return nil, &platform.Error{
Err: err,
}
}
return req, nil
}
// Valid validates that the view ID is non zero valued and update has expected values set.
func (r *patchViewRequest) Valid() *platform.Error {
if !r.ViewID.Valid() {
return &platform.Error{
Code: platform.EInvalid,
Msg: "missing view ID",
}
}
return r.Upd.Valid()
}