apigen: new endpoint to get project's single bucket rollup
Added new endpoint to get project's single bucket usage rollup. Extended generation code to handle service method args. Change-Id: Ief768632a801c047c66e0617056fbd7b30427b33
This commit is contained in:
parent
9eaeebe115
commit
1245283637
@ -9,9 +9,11 @@ import (
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/zeebo/errs"
|
||||
|
||||
"storj.io/common/uuid"
|
||||
"storj.io/storj/private/api"
|
||||
)
|
||||
|
||||
@ -74,12 +76,15 @@ func (a *API) generateGo() ([]byte, error) {
|
||||
p(`"context"`)
|
||||
p(`"encoding/json"`)
|
||||
p(`"net/http"`)
|
||||
p(`"strconv"`)
|
||||
p(`"time"`)
|
||||
p("")
|
||||
p(`"github.com/gorilla/mux"`)
|
||||
p(`"github.com/zeebo/errs"`)
|
||||
p(`"go.uber.org/zap"`)
|
||||
p("")
|
||||
|
||||
p(`"storj.io/common/uuid"`)
|
||||
p(`"storj.io/storj/private/api"`)
|
||||
|
||||
for _, group := range a.EndpointGroups {
|
||||
@ -110,9 +115,13 @@ func (a *API) generateGo() ([]byte, error) {
|
||||
p("")
|
||||
|
||||
p("type %sService interface {", group.Name)
|
||||
for _, method := range group.Endpoints {
|
||||
responseType := reflect.TypeOf(method.Response)
|
||||
p("%s(context.Context) (%s, api.HTTPError)", method.MethodName, a.handleTypesPackage(responseType))
|
||||
for _, e := range group.Endpoints {
|
||||
responseType := reflect.TypeOf(e.Response)
|
||||
var params string
|
||||
for _, param := range e.Params {
|
||||
params += param.Type.String() + ", "
|
||||
}
|
||||
p("%s(context.Context, "+params+") (%s, api.HTTPError)", e.MethodName, a.handleTypesPackage(responseType))
|
||||
}
|
||||
p("}")
|
||||
p("")
|
||||
@ -166,19 +175,47 @@ func (a *API) generateGo() ([]byte, error) {
|
||||
p("")
|
||||
}
|
||||
|
||||
methodFormat := "retVal, httpErr := h.service.%s(ctx"
|
||||
args := []string{endpoint.MethodName}
|
||||
|
||||
// TODO to be implemented
|
||||
// if !endpoint.NoAPIAuth {}
|
||||
|
||||
methodFormat += ")"
|
||||
interfaceArgs := make([]interface{}, len(args))
|
||||
for i, v := range args {
|
||||
interfaceArgs[i] = v
|
||||
for _, param := range endpoint.Params {
|
||||
switch param.Type {
|
||||
case reflect.TypeOf(uuid.UUID{}):
|
||||
p("%s, err := uuid.FromString(r.URL.Query().Get(\"%s\"))", param.Name, param.Name)
|
||||
p("if err != nil {")
|
||||
p("api.ServeError(h.log, w, http.StatusBadRequest, err)")
|
||||
p("return")
|
||||
p("}")
|
||||
p("")
|
||||
continue
|
||||
case reflect.TypeOf(time.Time{}):
|
||||
p("%sStamp, err := strconv.ParseInt(r.URL.Query().Get(\"%s\"), 10, 64)", param.Name, param.Name)
|
||||
p("if err != nil {")
|
||||
p("api.ServeError(h.log, w, http.StatusBadRequest, err)")
|
||||
p("return")
|
||||
p("}")
|
||||
p("")
|
||||
p("%s := time.Unix(%sStamp, 0).UTC()", param.Name, param.Name)
|
||||
p("")
|
||||
continue
|
||||
case reflect.TypeOf(""):
|
||||
p("%s := r.URL.Query().Get(\"%s\")", param.Name, param.Name)
|
||||
p("if %s == \"\" {", param.Name)
|
||||
p("api.ServeError(h.log, w, http.StatusBadRequest, errs.New(\"parameter '%s' can't be empty\"))", param.Name)
|
||||
p("return")
|
||||
p("}")
|
||||
p("")
|
||||
continue
|
||||
}
|
||||
}
|
||||
p(methodFormat, interfaceArgs...)
|
||||
p("if err != nil {")
|
||||
|
||||
methodFormat := "retVal, httpErr := h.service.%s(ctx, "
|
||||
for _, methodParam := range endpoint.Params {
|
||||
methodFormat += methodParam.Name + ", "
|
||||
}
|
||||
methodFormat += ")"
|
||||
p(methodFormat, endpoint.MethodName)
|
||||
p("if httpErr.Err != nil {")
|
||||
p("api.ServeError(h.log, w, httpErr.Status, httpErr.Err)")
|
||||
p("return")
|
||||
p("}")
|
||||
|
@ -3,7 +3,10 @@
|
||||
|
||||
package apigen
|
||||
|
||||
import "net/http"
|
||||
import (
|
||||
"net/http"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// Endpoint represents endpoint's configuration.
|
||||
type Endpoint struct {
|
||||
@ -14,7 +17,7 @@ type Endpoint struct {
|
||||
NoAPIAuth bool
|
||||
Request interface{}
|
||||
Response interface{}
|
||||
Params []string
|
||||
Params []Param
|
||||
}
|
||||
|
||||
// CookieAuth returns endpoint's cookie auth status.
|
||||
@ -54,3 +57,17 @@ func (eg *EndpointGroup) addEndpoint(path, method string, endpoint *Endpoint) {
|
||||
|
||||
eg.Endpoints[pathMethod] = endpoint
|
||||
}
|
||||
|
||||
// Param represents string interpretation of param's name and type.
|
||||
type Param struct {
|
||||
Name string
|
||||
Type reflect.Type
|
||||
}
|
||||
|
||||
// NewParam constructor which creates new Param entity by given name and type.
|
||||
func NewParam(name string, instance interface{}) Param {
|
||||
return Param{
|
||||
Name: name,
|
||||
Type: reflect.TypeOf(instance),
|
||||
}
|
||||
}
|
||||
|
@ -142,21 +142,21 @@ type BucketUsagePage struct {
|
||||
// BucketUsageRollup is total bucket usage info
|
||||
// for certain period.
|
||||
type BucketUsageRollup struct {
|
||||
ProjectID uuid.UUID
|
||||
BucketName string
|
||||
ProjectID uuid.UUID `json:"projectID"`
|
||||
BucketName string `json:"bucketName"`
|
||||
|
||||
TotalStoredData float64
|
||||
TotalStoredData float64 `json:"totalStoredData"`
|
||||
|
||||
TotalSegments float64
|
||||
ObjectCount float64
|
||||
MetadataSize float64
|
||||
TotalSegments float64 `json:"totalSegments"`
|
||||
ObjectCount float64 `json:"objectCount"`
|
||||
MetadataSize float64 `json:"metadataSize"`
|
||||
|
||||
RepairEgress float64
|
||||
GetEgress float64
|
||||
AuditEgress float64
|
||||
RepairEgress float64 `json:"repairEgress"`
|
||||
GetEgress float64 `json:"getEgress"`
|
||||
AuditEgress float64 `json:"auditEgress"`
|
||||
|
||||
Since time.Time
|
||||
Before time.Time
|
||||
Since time.Time `json:"since"`
|
||||
Before time.Time `json:"before"`
|
||||
}
|
||||
|
||||
// StoragenodeAccounting stores information about bandwidth and storage usage for storage nodes.
|
||||
|
@ -7,19 +7,24 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/zeebo/errs"
|
||||
"go.uber.org/zap"
|
||||
|
||||
"storj.io/common/uuid"
|
||||
"storj.io/storj/private/api"
|
||||
"storj.io/storj/satellite/accounting"
|
||||
"storj.io/storj/satellite/console"
|
||||
)
|
||||
|
||||
var ErrProjectsAPI = errs.Class("consoleapi projects api")
|
||||
|
||||
type ProjectManagementService interface {
|
||||
GetUserProjects(context.Context) ([]console.Project, api.HTTPError)
|
||||
GenGetUsersProjects(context.Context) ([]console.Project, api.HTTPError)
|
||||
GenGetSingleBucketUsageRollup(context.Context, uuid.UUID, string, time.Time, time.Time) (*accounting.BucketUsageRollup, api.HTTPError)
|
||||
}
|
||||
|
||||
// Handler is an api handler that exposes all projects related functionality.
|
||||
@ -37,12 +42,13 @@ func NewProjectManagement(log *zap.Logger, service ProjectManagementService, rou
|
||||
}
|
||||
|
||||
projectsRouter := router.PathPrefix("/api/v0/projects").Subrouter()
|
||||
projectsRouter.HandleFunc("/", handler.handleGetUserProjects).Methods("GET")
|
||||
projectsRouter.HandleFunc("/", handler.handleGenGetUsersProjects).Methods("GET")
|
||||
projectsRouter.HandleFunc("/bucket-rollup", handler.handleGenGetSingleBucketUsageRollup).Methods("GET")
|
||||
|
||||
return handler
|
||||
}
|
||||
|
||||
func (h *Handler) handleGetUserProjects(w http.ResponseWriter, r *http.Request) {
|
||||
func (h *Handler) handleGenGetUsersProjects(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
var err error
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
@ -55,14 +61,67 @@ func (h *Handler) handleGetUserProjects(w http.ResponseWriter, r *http.Request)
|
||||
return
|
||||
}
|
||||
|
||||
retVal, httpErr := h.service.GetUserProjects(ctx)
|
||||
if err != nil {
|
||||
retVal, httpErr := h.service.GenGetUsersProjects(ctx)
|
||||
if httpErr.Err != nil {
|
||||
api.ServeError(h.log, w, httpErr.Status, httpErr.Err)
|
||||
return
|
||||
}
|
||||
|
||||
err = json.NewEncoder(w).Encode(retVal)
|
||||
if err != nil {
|
||||
h.log.Debug("failed to write json GetUserProjects response", zap.Error(ErrProjectsAPI.Wrap(err)))
|
||||
h.log.Debug("failed to write json GenGetUsersProjects response", zap.Error(ErrProjectsAPI.Wrap(err)))
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Handler) handleGenGetSingleBucketUsageRollup(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
var err error
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
ctx, err = h.auth.IsAuthenticated(ctx, r)
|
||||
if err != nil {
|
||||
api.ServeError(h.log, w, http.StatusUnauthorized, err)
|
||||
return
|
||||
}
|
||||
|
||||
projectID, err := uuid.FromString(r.URL.Query().Get("projectID"))
|
||||
if err != nil {
|
||||
api.ServeError(h.log, w, http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
|
||||
bucket := r.URL.Query().Get("bucket")
|
||||
if bucket == "" {
|
||||
api.ServeError(h.log, w, http.StatusBadRequest, errs.New("parameter 'bucket' can't be empty"))
|
||||
return
|
||||
}
|
||||
|
||||
sinceStamp, err := strconv.ParseInt(r.URL.Query().Get("since"), 10, 64)
|
||||
if err != nil {
|
||||
api.ServeError(h.log, w, http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
|
||||
since := time.Unix(sinceStamp, 0).UTC()
|
||||
|
||||
beforeStamp, err := strconv.ParseInt(r.URL.Query().Get("before"), 10, 64)
|
||||
if err != nil {
|
||||
api.ServeError(h.log, w, http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
|
||||
before := time.Unix(beforeStamp, 0).UTC()
|
||||
|
||||
retVal, httpErr := h.service.GenGetSingleBucketUsageRollup(ctx, projectID, bucket, since, before)
|
||||
if httpErr.Err != nil {
|
||||
api.ServeError(h.log, w, httpErr.Status, httpErr.Err)
|
||||
return
|
||||
}
|
||||
|
||||
err = json.NewEncoder(w).Encode(retVal)
|
||||
if err != nil {
|
||||
h.log.Debug("failed to write json GenGetSingleBucketUsageRollup response", zap.Error(ErrProjectsAPI.Wrap(err)))
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,11 @@ package main
|
||||
//go:generate go run ./
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"storj.io/common/uuid"
|
||||
"storj.io/storj/private/apigen"
|
||||
"storj.io/storj/satellite/accounting"
|
||||
"storj.io/storj/satellite/console"
|
||||
)
|
||||
|
||||
@ -23,10 +27,22 @@ func main() {
|
||||
g.Get("/", &apigen.Endpoint{
|
||||
Name: "Get Projects",
|
||||
Description: "Gets all projects user has",
|
||||
MethodName: "GetUserProjects",
|
||||
MethodName: "GenGetUsersProjects",
|
||||
Response: []console.Project{},
|
||||
})
|
||||
|
||||
g.Get("/bucket-rollup", &apigen.Endpoint{
|
||||
Name: "Get Project's Bucket Usage",
|
||||
Description: "Gets project's bucket usage by bucket ID",
|
||||
MethodName: "GenGetSingleBucketUsageRollup",
|
||||
Response: &accounting.BucketUsageRollup{},
|
||||
Params: []apigen.Param{
|
||||
apigen.NewParam("projectID", uuid.UUID{}),
|
||||
apigen.NewParam("bucket", ""),
|
||||
apigen.NewParam("since", time.Time{}),
|
||||
apigen.NewParam("before", time.Time{}),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
a.MustWrite("satellite/console/consoleweb/consoleapi/api.gen.go")
|
||||
|
@ -212,6 +212,10 @@ func NewServer(logger *zap.Logger, config Config, service *console.Service, mail
|
||||
router := mux.NewRouter()
|
||||
fs := http.FileServer(http.Dir(server.config.StaticDir))
|
||||
|
||||
if server.config.GeneratedAPIEnabled {
|
||||
consoleapi.NewProjectManagement(logger, server.service, router, server.service)
|
||||
}
|
||||
|
||||
router.HandleFunc("/registrationToken/", server.createRegistrationTokenHandler)
|
||||
router.HandleFunc("/robots.txt", server.seoHandler)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user