apigen: endpint to update project

Implemented new endpoint for project update using apigen.
Implemented new service method compatible with new generated api.

Change-Id: Ic0a7e0bbf3ea942275bd927d6e30cfb7e721e9c1
This commit is contained in:
NickolaiYurchenko 2022-04-07 11:05:28 +03:00 committed by Maximillian von Briesen
parent c944c3f289
commit c32ca6e67f
5 changed files with 241 additions and 49 deletions

View File

@ -227,6 +227,24 @@ func (a *API) generateGo() ([]byte, error) {
p("}")
p("")
}
case http.MethodPatch:
for _, param := range endpoint.Params {
if param.Type == 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("")
} else {
p("%s := &%s{}", param.Name, param.Type)
p("if err = json.NewDecoder(r.Body).Decode(&%s); err != nil {", param.Name)
p("api.ServeError(h.log, w, http.StatusBadRequest, err)")
p("return")
p("}")
p("")
}
}
}
methodFormat := "retVal, httpErr := h.service.%s(ctx, "
@ -239,6 +257,14 @@ func (a *API) generateGo() ([]byte, error) {
for _, methodParam := range endpoint.Params {
methodFormat += "*" + methodParam.Name + ", "
}
case http.MethodPatch:
for _, methodParam := range endpoint.Params {
if methodParam.Type == reflect.TypeOf(uuid.UUID{}) {
methodFormat += methodParam.Name + ", "
} else {
methodFormat += "*" + methodParam.Name + ", "
}
}
}
methodFormat += ")"

View File

@ -48,6 +48,11 @@ func (eg *EndpointGroup) Get(path string, endpoint *Endpoint) {
eg.addEndpoint(path, http.MethodGet, endpoint)
}
// Patch adds new PATCH endpoint to endpoints group.
func (eg *EndpointGroup) Patch(path string, endpoint *Endpoint) {
eg.addEndpoint(path, http.MethodPatch, endpoint)
}
// Put adds new PUT endpoint to endpoints group.
func (eg *EndpointGroup) Put(path string, endpoint *Endpoint) {
eg.addEndpoint(path, http.MethodPut, endpoint)

View File

@ -23,10 +23,11 @@ import (
var ErrProjectsAPI = errs.Class("consoleapi projects api")
type ProjectManagementService interface {
GenCreateProject(context.Context, console.ProjectInfo) (*console.Project, api.HTTPError)
GenUpdateProject(context.Context, uuid.UUID, console.ProjectInfo) (*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)
GenGetBucketUsageRollups(context.Context, uuid.UUID, time.Time, time.Time) ([]accounting.BucketUsageRollup, api.HTTPError)
GenCreateProject(context.Context, console.ProjectInfo) (*console.Project, api.HTTPError)
}
// Handler is an api handler that exposes all projects related functionality.
@ -44,14 +45,62 @@ func NewProjectManagement(log *zap.Logger, service ProjectManagementService, rou
}
projectsRouter := router.PathPrefix("/api/v0/projects").Subrouter()
projectsRouter.HandleFunc("/create", handler.handleGenCreateProject).Methods("PUT")
projectsRouter.HandleFunc("/", handler.handleGenGetUsersProjects).Methods("GET")
projectsRouter.HandleFunc("/bucket-rollup", handler.handleGenGetSingleBucketUsageRollup).Methods("GET")
projectsRouter.HandleFunc("/bucket-rollups", handler.handleGenGetBucketUsageRollups).Methods("GET")
projectsRouter.HandleFunc("/create", handler.handleGenCreateProject).Methods("PUT")
projectsRouter.HandleFunc("/update", handler.handleGenUpdateProject).Methods("PATCH")
return handler
}
func (h *Handler) handleGenGetBucketUsageRollups(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, true, true)
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
}
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.GenGetBucketUsageRollups(ctx, projectID, 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 GenGetBucketUsageRollups response", zap.Error(ErrProjectsAPI.Wrap(err)))
}
}
func (h *Handler) handleGenCreateProject(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
var err error
@ -83,6 +132,43 @@ func (h *Handler) handleGenCreateProject(w http.ResponseWriter, r *http.Request)
}
}
func (h *Handler) handleGenUpdateProject(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, true, true)
if err != nil {
api.ServeError(h.log, w, http.StatusUnauthorized, err)
return
}
id, err := uuid.FromString(r.URL.Query().Get("id"))
if err != nil {
api.ServeError(h.log, w, http.StatusBadRequest, err)
return
}
projectInfo := &console.ProjectInfo{}
if err = json.NewDecoder(r.Body).Decode(&projectInfo); err != nil {
api.ServeError(h.log, w, http.StatusBadRequest, err)
return
}
retVal, httpErr := h.service.GenUpdateProject(ctx, id, *projectInfo)
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 GenUpdateProject response", zap.Error(ErrProjectsAPI.Wrap(err)))
}
}
func (h *Handler) handleGenGetUsersProjects(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
var err error
@ -160,50 +246,3 @@ func (h *Handler) handleGenGetSingleBucketUsageRollup(w http.ResponseWriter, r *
h.log.Debug("failed to write json GenGetSingleBucketUsageRollup response", zap.Error(ErrProjectsAPI.Wrap(err)))
}
}
func (h *Handler) handleGenGetBucketUsageRollups(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, true, true)
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
}
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.GenGetBucketUsageRollups(ctx, projectID, 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 GenGetBucketUsageRollups response", zap.Error(ErrProjectsAPI.Wrap(err)))
}
}

View File

@ -65,6 +65,17 @@ func main() {
apigen.NewParam("projectInfo", console.ProjectInfo{}),
},
})
g.Patch("/update", &apigen.Endpoint{
Name: "Update Project",
Description: "Updates project with given info",
MethodName: "GenUpdateProject",
Response: &console.Project{},
Params: []apigen.Param{
apigen.NewParam("id", uuid.UUID{}),
apigen.NewParam("projectInfo", console.ProjectInfo{}),
},
})
}
a.MustWrite("satellite/console/consoleweb/consoleapi/api.gen.go")

View File

@ -1388,6 +1388,117 @@ func (s *Service) UpdateProject(ctx context.Context, projectID uuid.UUID, projec
return project, nil
}
// GenUpdateProject is a method for updating project name and description by id for generated api.
func (s *Service) GenUpdateProject(ctx context.Context, projectID uuid.UUID, projectInfo ProjectInfo) (p *Project, httpError api.HTTPError) {
var err error
defer mon.Task()(&ctx)(&err)
auth, err := s.getAuthAndAuditLog(ctx, "update project name and description", zap.String("projectID", projectID.String()))
if err != nil {
return nil, api.HTTPError{
Status: http.StatusUnauthorized,
Err: Error.Wrap(err),
}
}
err = ValidateNameAndDescription(projectInfo.Name, projectInfo.Description)
if err != nil {
return nil, api.HTTPError{
Status: http.StatusBadRequest,
Err: Error.Wrap(err),
}
}
isMember, err := s.isProjectMember(ctx, auth.User.ID, projectID)
if err != nil {
return nil, api.HTTPError{
Status: http.StatusUnauthorized,
Err: Error.Wrap(err),
}
}
project := isMember.project
project.Name = projectInfo.Name
project.Description = projectInfo.Description
if auth.User.PaidTier {
if project.BandwidthLimit != nil && *project.BandwidthLimit == 0 {
return nil, api.HTTPError{
Status: http.StatusInternalServerError,
Err: Error.New("current bandwidth limit for project is set to 0 (updating disabled)"),
}
}
if project.StorageLimit != nil && *project.StorageLimit == 0 {
return nil, api.HTTPError{
Status: http.StatusInternalServerError,
Err: Error.New("current storage limit for project is set to 0 (updating disabled)"),
}
}
if projectInfo.StorageLimit <= 0 || projectInfo.BandwidthLimit <= 0 {
return nil, api.HTTPError{
Status: http.StatusBadRequest,
Err: Error.New("project limits must be greater than 0"),
}
}
if projectInfo.StorageLimit > s.config.UsageLimits.Storage.Paid {
return nil, api.HTTPError{
Status: http.StatusBadRequest,
Err: Error.New("specified storage limit exceeds allowed maximum for current tier"),
}
}
if projectInfo.BandwidthLimit > s.config.UsageLimits.Bandwidth.Paid {
return nil, api.HTTPError{
Status: http.StatusBadRequest,
Err: Error.New("specified bandwidth limit exceeds allowed maximum for current tier"),
}
}
storageUsed, err := s.projectUsage.GetProjectStorageTotals(ctx, projectID)
if err != nil {
return nil, api.HTTPError{
Status: http.StatusInternalServerError,
Err: Error.Wrap(err),
}
}
if projectInfo.StorageLimit.Int64() < storageUsed {
return nil, api.HTTPError{
Status: http.StatusBadRequest,
Err: Error.New("cannot set storage limit below current usage"),
}
}
bandwidthUsed, err := s.projectUsage.GetProjectBandwidthTotals(ctx, projectID)
if err != nil {
return nil, api.HTTPError{
Status: http.StatusInternalServerError,
Err: Error.Wrap(err),
}
}
if projectInfo.BandwidthLimit.Int64() < bandwidthUsed {
return nil, api.HTTPError{
Status: http.StatusBadRequest,
Err: Error.New("cannot set bandwidth limit below current usage"),
}
}
project.StorageLimit = new(memory.Size)
*project.StorageLimit = projectInfo.StorageLimit
project.BandwidthLimit = new(memory.Size)
*project.BandwidthLimit = projectInfo.BandwidthLimit
}
err = s.store.Projects().Update(ctx, project)
if err != nil {
return nil, api.HTTPError{
Status: http.StatusInternalServerError,
Err: Error.Wrap(err),
}
}
return project, httpError
}
// AddProjectMembers adds users by email to given project.
func (s *Service) AddProjectMembers(ctx context.Context, projectID uuid.UUID, emails []string) (users []*User, err error) {
defer mon.Task()(&ctx)(&err)