influxdb/tenant/http_server_org.go

232 lines
5.8 KiB
Go

package tenant
import (
"context"
"fmt"
"net/http"
"github.com/go-chi/chi"
"github.com/go-chi/chi/middleware"
"github.com/influxdata/influxdb/v2"
"github.com/influxdata/influxdb/v2/kit/platform"
kithttp "github.com/influxdata/influxdb/v2/kit/transport/http"
"go.uber.org/zap"
)
// OrgHandler represents an HTTP API handler for organizations.
type OrgHandler struct {
chi.Router
api *kithttp.API
log *zap.Logger
orgSvc influxdb.OrganizationService
}
const (
prefixOrganizations = "/api/v2/orgs"
)
func (h *OrgHandler) Prefix() string {
return prefixOrganizations
}
// NewHTTPOrgHandler constructs a new http server.
func NewHTTPOrgHandler(log *zap.Logger, orgService influxdb.OrganizationService, urm http.Handler, secretHandler http.Handler) *OrgHandler {
svr := &OrgHandler{
api: kithttp.NewAPI(kithttp.WithLog(log)),
log: log,
orgSvc: orgService,
}
r := chi.NewRouter()
r.Use(
middleware.Recoverer,
middleware.RequestID,
middleware.RealIP,
)
r.Route("/", func(r chi.Router) {
r.Post("/", svr.handlePostOrg)
r.Get("/", svr.handleGetOrgs)
r.Route("/{id}", func(r chi.Router) {
r.Get("/", svr.handleGetOrg)
r.Patch("/", svr.handlePatchOrg)
r.Delete("/", svr.handleDeleteOrg)
// mount embedded resources
mountableRouter := r.With(kithttp.ValidResource(svr.api, svr.lookupOrgByID))
mountableRouter.Mount("/members", urm)
mountableRouter.Mount("/owners", urm)
mountableRouter.Mount("/secrets", secretHandler)
})
})
svr.Router = r
return svr
}
type orgResponse struct {
Links map[string]string `json:"links"`
influxdb.Organization
}
func newOrgResponse(o influxdb.Organization) orgResponse {
return orgResponse{
Links: map[string]string{
"self": fmt.Sprintf("/api/v2/orgs/%s", o.ID),
"logs": fmt.Sprintf("/api/v2/orgs/%s/logs", o.ID),
"members": fmt.Sprintf("/api/v2/orgs/%s/members", o.ID),
"owners": fmt.Sprintf("/api/v2/orgs/%s/owners", o.ID),
"secrets": fmt.Sprintf("/api/v2/orgs/%s/secrets", o.ID),
"labels": fmt.Sprintf("/api/v2/orgs/%s/labels", o.ID),
"buckets": fmt.Sprintf("/api/v2/buckets?org=%s", o.Name),
"tasks": fmt.Sprintf("/api/v2/tasks?org=%s", o.Name),
"dashboards": fmt.Sprintf("/api/v2/dashboards?org=%s", o.Name),
},
Organization: o,
}
}
type orgsResponse struct {
Links map[string]string `json:"links"`
Organizations []orgResponse `json:"orgs"`
}
func newOrgsResponse(orgs []*influxdb.Organization) *orgsResponse {
res := orgsResponse{
Links: map[string]string{
"self": "/api/v2/orgs",
},
Organizations: []orgResponse{},
}
for _, org := range orgs {
res.Organizations = append(res.Organizations, newOrgResponse(*org))
}
return &res
}
// handlePostOrg is the HTTP handler for the POST /api/v2/orgs route.
func (h *OrgHandler) handlePostOrg(w http.ResponseWriter, r *http.Request) {
var org influxdb.Organization
if err := h.api.DecodeJSON(r.Body, &org); err != nil {
h.api.Err(w, r, err)
return
}
if err := h.orgSvc.CreateOrganization(r.Context(), &org); err != nil {
h.api.Err(w, r, err)
return
}
h.log.Debug("Org created", zap.String("org", fmt.Sprint(org)))
h.api.Respond(w, r, http.StatusCreated, newOrgResponse(org))
}
// handleGetOrg is the HTTP handler for the GET /api/v2/orgs/:id route.
func (h *OrgHandler) handleGetOrg(w http.ResponseWriter, r *http.Request) {
id, err := platform.IDFromString(chi.URLParam(r, "id"))
if err != nil {
h.api.Err(w, r, err)
return
}
org, err := h.orgSvc.FindOrganizationByID(r.Context(), *id)
if err != nil {
h.api.Err(w, r, err)
return
}
h.log.Debug("Org retrieved", zap.String("org", fmt.Sprint(org)))
h.api.Respond(w, r, http.StatusOK, newOrgResponse(*org))
}
// handleGetOrgs is the HTTP handler for the GET /api/v2/orgs route.
func (h *OrgHandler) handleGetOrgs(w http.ResponseWriter, r *http.Request) {
qp := r.URL.Query()
opts, err := influxdb.DecodeFindOptions(r)
if err != nil {
h.api.Err(w, r, err)
return
}
var filter influxdb.OrganizationFilter
if name := qp.Get("org"); name != "" {
filter.Name = &name
}
if id := qp.Get("orgID"); id != "" {
i, err := platform.IDFromString(id)
if err == nil {
filter.ID = i
}
}
if id := qp.Get("userID"); id != "" {
i, err := platform.IDFromString(id)
if err == nil {
filter.UserID = i
}
}
orgs, _, err := h.orgSvc.FindOrganizations(r.Context(), filter, *opts)
if err != nil {
h.api.Err(w, r, err)
return
}
h.log.Debug("Orgs retrieved", zap.String("org", fmt.Sprint(orgs)))
h.api.Respond(w, r, http.StatusOK, newOrgsResponse(orgs))
}
// handlePatchOrg is the HTTP handler for the PATH /api/v2/orgs route.
func (h *OrgHandler) handlePatchOrg(w http.ResponseWriter, r *http.Request) {
id, err := platform.IDFromString(chi.URLParam(r, "id"))
if err != nil {
h.api.Err(w, r, err)
return
}
var upd influxdb.OrganizationUpdate
if err := h.api.DecodeJSON(r.Body, &upd); err != nil {
h.api.Err(w, r, err)
return
}
org, err := h.orgSvc.UpdateOrganization(r.Context(), *id, upd)
if err != nil {
h.api.Err(w, r, err)
return
}
h.log.Debug("Org updated", zap.String("org", fmt.Sprint(org)))
h.api.Respond(w, r, http.StatusOK, newOrgResponse(*org))
}
// handleDeleteOrganization is the HTTP handler for the DELETE /api/v2/orgs/:id route.
func (h *OrgHandler) handleDeleteOrg(w http.ResponseWriter, r *http.Request) {
id, err := platform.IDFromString(chi.URLParam(r, "id"))
if err != nil {
h.api.Err(w, r, err)
return
}
ctx := r.Context()
if err := h.orgSvc.DeleteOrganization(ctx, *id); err != nil {
h.api.Err(w, r, err)
return
}
h.log.Debug("Org deleted", zap.String("orgID", fmt.Sprint(id)))
h.api.Respond(w, r, http.StatusNoContent, nil)
}
func (h *OrgHandler) lookupOrgByID(ctx context.Context, id platform.ID) (platform.ID, error) {
_, err := h.orgSvc.FindOrganizationByID(ctx, id)
if err != nil {
return 0, err
}
return id, nil
}