361 lines
8.9 KiB
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()
|
|
}
|