influxdb/http/view_service.go

408 lines
10 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/influxdb"
"github.com/julienschmidt/httprouter"
"go.uber.org/zap"
)
// ViewBackend is all services and associated parameters required to construct
// the ScraperHandler.
type ViewBackend struct {
Logger *zap.Logger
ViewService influxdb.ViewService
UserService influxdb.UserService
UserResourceMappingService influxdb.UserResourceMappingService
LabelService influxdb.LabelService
}
// NewViewBackend returns a new instance of ViewBackend.
func NewViewBackend(b *APIBackend) *ViewBackend {
return &ViewBackend{
Logger: b.Logger.With(zap.String("handler", "scraper")),
ViewService: b.ViewService,
UserService: b.UserService,
LabelService: b.LabelService,
}
}
// ViewHandler is the handler for the view service
type ViewHandler struct {
*httprouter.Router
Logger *zap.Logger
ViewService influxdb.ViewService
UserService influxdb.UserService
UserResourceMappingService influxdb.UserResourceMappingService
LabelService influxdb.LabelService
}
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"
viewsIDLabelsIDPath = "/api/v2/views/:id/labels/:lid"
)
// NewViewHandler returns a new instance of ViewHandler.
func NewViewHandler(b *ViewBackend) *ViewHandler {
h := &ViewHandler{
Router: NewRouter(),
Logger: b.Logger,
ViewService: b.ViewService,
UserResourceMappingService: b.UserResourceMappingService,
LabelService: b.LabelService,
UserService: b.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)
memberBackend := MemberBackend{
Logger: b.Logger.With(zap.String("handler", "member")),
ResourceType: influxdb.ViewsResourceType,
UserType: influxdb.Member,
UserResourceMappingService: b.UserResourceMappingService,
UserService: b.UserService,
}
h.HandlerFunc("POST", viewsIDMembersPath, newPostMemberHandler(memberBackend))
h.HandlerFunc("GET", viewsIDMembersPath, newGetMembersHandler(memberBackend))
h.HandlerFunc("DELETE", viewsIDMembersIDPath, newDeleteMemberHandler(memberBackend))
ownerBackend := MemberBackend{
Logger: b.Logger.With(zap.String("handler", "member")),
ResourceType: influxdb.ViewsResourceType,
UserType: influxdb.Owner,
UserResourceMappingService: b.UserResourceMappingService,
UserService: b.UserService,
}
h.HandlerFunc("POST", viewsIDOwnersPath, newPostMemberHandler(ownerBackend))
h.HandlerFunc("GET", viewsIDOwnersPath, newGetMembersHandler(ownerBackend))
h.HandlerFunc("DELETE", viewsIDOwnersIDPath, newDeleteMemberHandler(ownerBackend))
labelBackend := &LabelBackend{
Logger: b.Logger.With(zap.String("handler", "label")),
LabelService: b.LabelService,
}
h.HandlerFunc("GET", viewsIDLabelsPath, newGetLabelsHandler(labelBackend))
h.HandlerFunc("POST", viewsIDLabelsPath, newPostLabelHandler(labelBackend))
h.HandlerFunc("DELETE", viewsIDLabelsIDPath, newDeleteLabelHandler(labelBackend))
return h
}
type viewLinks struct {
Self string `json:"self"`
Labels string `json:"labels"`
}
type viewResponse struct {
influxdb.View
Links viewLinks `json:"links"`
}
func (r viewResponse) MarshalJSON() ([]byte, error) {
props, err := influxdb.MarshalViewPropertiesJSON(r.Properties)
if err != nil {
return nil, err
}
return json.Marshal(struct {
influxdb.ViewContents
Links viewLinks `json:"links"`
Properties json.RawMessage `json:"properties"`
}{
ViewContents: r.ViewContents,
Links: r.Links,
Properties: props,
})
}
func newViewResponse(c *influxdb.View) viewResponse {
return viewResponse{
Links: viewLinks{
Self: fmt.Sprintf("/api/v2/views/%s", c.ID),
Labels: fmt.Sprintf("/api/v2/views/%s/labels", 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 influxdb.ViewFilter
}
func decodeGetViewsRequest(ctx context.Context, r *http.Request) *getViewsRequest {
qp := r.URL.Query()
return &getViewsRequest{
filter: influxdb.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 []*influxdb.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 *influxdb.View
}
func decodePostViewRequest(ctx context.Context, r *http.Request) (*postViewRequest, error) {
c := &influxdb.View{}
if err := json.NewDecoder(r.Body).Decode(c); err != nil {
return nil, &influxdb.Error{
Code: influxdb.EInvalid,
Msg: err.Error(),
}
}
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 influxdb.ID
}
func decodeGetViewRequest(ctx context.Context, r *http.Request) (*getViewRequest, error) {
params := httprouter.ParamsFromContext(ctx)
id := params.ByName("id")
if id == "" {
return nil, &influxdb.Error{
Code: influxdb.EInvalid,
Msg: "url missing id",
}
}
var i influxdb.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 influxdb.ID
}
func decodeDeleteViewRequest(ctx context.Context, r *http.Request) (*deleteViewRequest, error) {
params := httprouter.ParamsFromContext(ctx)
id := params.ByName("id")
if id == "" {
return nil, &influxdb.Error{
Code: influxdb.EInvalid,
Msg: "url missing id",
}
}
var i influxdb.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 influxdb.ID
Upd influxdb.ViewUpdate
}
func decodePatchViewRequest(ctx context.Context, r *http.Request) (*patchViewRequest, *influxdb.Error) {
req := &patchViewRequest{}
upd := influxdb.ViewUpdate{}
if err := json.NewDecoder(r.Body).Decode(&upd); err != nil {
return nil, &influxdb.Error{
Code: influxdb.EInvalid,
Msg: err.Error(),
}
}
req.Upd = upd
params := httprouter.ParamsFromContext(ctx)
id := params.ByName("id")
if id == "" {
return nil, &influxdb.Error{
Code: influxdb.EInvalid,
Msg: "url missing id",
}
}
var i influxdb.ID
if err := i.DecodeFromString(id); err != nil {
return nil, &influxdb.Error{
Code: influxdb.EInvalid,
Err: err,
}
}
req.ViewID = i
if err := req.Valid(); err != nil {
return nil, &influxdb.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() *influxdb.Error {
if !r.ViewID.Valid() {
return &influxdb.Error{
Code: influxdb.EInvalid,
Msg: "missing view ID",
}
}
return r.Upd.Valid()
}