*: implement update projectgroup
This commit is contained in:
parent
749109cd75
commit
6c11ab0596
|
@ -73,18 +73,40 @@ func (h *ActionHandler) GetProjectGroupProjects(ctx context.Context, projectGrou
|
|||
return projects, nil
|
||||
}
|
||||
|
||||
func (h *ActionHandler) CreateProjectGroup(ctx context.Context, projectGroup *types.ProjectGroup) (*types.ProjectGroup, error) {
|
||||
if projectGroup.Name == "" {
|
||||
return nil, util.NewErrBadRequest(errors.Errorf("project group name required"))
|
||||
}
|
||||
if !util.ValidateName(projectGroup.Name) {
|
||||
return nil, util.NewErrBadRequest(errors.Errorf("invalid project group name %q", projectGroup.Name))
|
||||
func (h *ActionHandler) ValidateProjectGroup(ctx context.Context, projectGroup *types.ProjectGroup) error {
|
||||
if projectGroup.Parent.Type != types.ConfigTypeProjectGroup &&
|
||||
projectGroup.Parent.Type != types.ConfigTypeOrg &&
|
||||
projectGroup.Parent.Type != types.ConfigTypeUser {
|
||||
return util.NewErrBadRequest(errors.Errorf("invalid project group parent type %q", projectGroup.Parent.Type))
|
||||
}
|
||||
if projectGroup.Parent.ID == "" {
|
||||
return nil, util.NewErrBadRequest(errors.Errorf("project group parent id required"))
|
||||
return util.NewErrBadRequest(errors.Errorf("project group parent id required"))
|
||||
}
|
||||
|
||||
// if the project group is a root project group the name must be empty
|
||||
if projectGroup.Parent.Type == types.ConfigTypeOrg ||
|
||||
projectGroup.Parent.Type == types.ConfigTypeUser {
|
||||
if projectGroup.Name != "" {
|
||||
return util.NewErrBadRequest(errors.Errorf("project group name for root project group must be empty"))
|
||||
}
|
||||
} else {
|
||||
if projectGroup.Name == "" {
|
||||
return util.NewErrBadRequest(errors.Errorf("project group name required"))
|
||||
}
|
||||
if !util.ValidateName(projectGroup.Name) {
|
||||
return util.NewErrBadRequest(errors.Errorf("invalid project group name %q", projectGroup.Name))
|
||||
}
|
||||
}
|
||||
if !types.IsValidVisibility(projectGroup.Visibility) {
|
||||
return nil, util.NewErrBadRequest(errors.Errorf("invalid project group visibility"))
|
||||
return util.NewErrBadRequest(errors.Errorf("invalid project group visibility"))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *ActionHandler) CreateProjectGroup(ctx context.Context, projectGroup *types.ProjectGroup) (*types.ProjectGroup, error) {
|
||||
if err := h.ValidateProjectGroup(ctx, projectGroup); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var cgt *datamanager.ChangeGroupsUpdateToken
|
||||
|
@ -131,7 +153,7 @@ func (h *ActionHandler) CreateProjectGroup(ctx context.Context, projectGroup *ty
|
|||
projectGroup.ID = uuid.NewV4().String()
|
||||
projectGroup.Parent.Type = types.ConfigTypeProjectGroup
|
||||
|
||||
pcj, err := json.Marshal(projectGroup)
|
||||
pgj, err := json.Marshal(projectGroup)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to marshal projectGroup")
|
||||
}
|
||||
|
@ -140,7 +162,7 @@ func (h *ActionHandler) CreateProjectGroup(ctx context.Context, projectGroup *ty
|
|||
ActionType: datamanager.ActionTypePut,
|
||||
DataType: string(types.ConfigTypeProjectGroup),
|
||||
ID: projectGroup.ID,
|
||||
Data: pcj,
|
||||
Data: pgj,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -148,6 +170,98 @@ func (h *ActionHandler) CreateProjectGroup(ctx context.Context, projectGroup *ty
|
|||
return projectGroup, err
|
||||
}
|
||||
|
||||
type UpdateProjectGroupRequest struct {
|
||||
ProjectGroupRef string
|
||||
|
||||
ProjectGroup *types.ProjectGroup
|
||||
}
|
||||
|
||||
func (h *ActionHandler) UpdateProjectGroup(ctx context.Context, req *UpdateProjectGroupRequest) (*types.ProjectGroup, error) {
|
||||
if err := h.ValidateProjectGroup(ctx, req.ProjectGroup); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var cgt *datamanager.ChangeGroupsUpdateToken
|
||||
|
||||
// must do all the checks in a single transaction to avoid concurrent changes
|
||||
err := h.readDB.Do(func(tx *db.Tx) error {
|
||||
var err error
|
||||
// check project exists
|
||||
pg, err := h.readDB.GetProjectGroup(tx, req.ProjectGroupRef)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if pg == nil {
|
||||
return util.NewErrBadRequest(errors.Errorf("project group with ref %q doesn't exist", req.ProjectGroupRef))
|
||||
}
|
||||
// check that the project.ID matches
|
||||
if pg.ID != req.ProjectGroup.ID {
|
||||
return util.NewErrBadRequest(errors.Errorf("project group with ref %q has a different id", req.ProjectGroupRef))
|
||||
}
|
||||
|
||||
// check parent exists
|
||||
switch pg.Parent.Type {
|
||||
case types.ConfigTypeProjectGroup:
|
||||
group, err := h.readDB.GetProjectGroup(tx, req.ProjectGroup.Parent.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if group == nil {
|
||||
return util.NewErrBadRequest(errors.Errorf("project group with id %q doesn't exist", req.ProjectGroup.Parent.ID))
|
||||
}
|
||||
}
|
||||
|
||||
// currently we don't support changing parent
|
||||
// TODO(sgotti) handle project move (changed parent project group)
|
||||
if pg.Parent.Type != req.ProjectGroup.Parent.Type {
|
||||
return util.NewErrBadRequest(errors.Errorf("changing project group parent isn't supported"))
|
||||
}
|
||||
if pg.Parent.ID != req.ProjectGroup.Parent.ID {
|
||||
return util.NewErrBadRequest(errors.Errorf("changing project group parent isn't supported"))
|
||||
}
|
||||
|
||||
// if the project group is a root project group force the name to be empty
|
||||
if pg.Parent.Type == types.ConfigTypeOrg ||
|
||||
pg.Parent.Type == types.ConfigTypeUser {
|
||||
req.ProjectGroup.Name = ""
|
||||
}
|
||||
|
||||
pgp, err := h.readDB.GetProjectGroupPath(tx, pg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// changegroup is the project group path. Use "projectpath" prefix as it must
|
||||
// cover both projects and projectgroups
|
||||
cgNames := []string{util.EncodeSha256Hex("projectpath-" + pgp)}
|
||||
cgt, err = h.readDB.GetChangeGroupsUpdateTokens(tx, cgNames)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pgj, err := json.Marshal(req.ProjectGroup)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to marshal project")
|
||||
}
|
||||
actions := []*datamanager.Action{
|
||||
{
|
||||
ActionType: datamanager.ActionTypePut,
|
||||
DataType: string(types.ConfigTypeProjectGroup),
|
||||
ID: req.ProjectGroup.ID,
|
||||
Data: pgj,
|
||||
},
|
||||
}
|
||||
|
||||
_, err = h.dm.WriteWal(ctx, actions, cgt)
|
||||
return req.ProjectGroup, err
|
||||
}
|
||||
|
||||
func (h *ActionHandler) DeleteProjectGroup(ctx context.Context, projectGroupRef string) error {
|
||||
var projectGroup *types.ProjectGroup
|
||||
|
||||
|
|
|
@ -135,6 +135,17 @@ func (c *Client) CreateProjectGroup(ctx context.Context, projectGroup *types.Pro
|
|||
return resProjectGroup, resp, err
|
||||
}
|
||||
|
||||
func (c *Client) UpdateProjectGroup(ctx context.Context, projectGroupRef string, projectGroup *types.ProjectGroup) (*ProjectGroup, *http.Response, error) {
|
||||
pj, err := json.Marshal(projectGroup)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
resProjectGroup := new(ProjectGroup)
|
||||
resp, err := c.getParsedResponse(ctx, "PUT", fmt.Sprintf("/projectgroups/%s", url.PathEscape(projectGroupRef)), nil, jsonContent, bytes.NewReader(pj), resProjectGroup)
|
||||
return resProjectGroup, resp, err
|
||||
}
|
||||
|
||||
func (c *Client) DeleteProjectGroup(ctx context.Context, projectGroupRef string) (*http.Response, error) {
|
||||
return c.getResponse(ctx, "DELETE", fmt.Sprintf("/projectgroups/%s", url.PathEscape(projectGroupRef)), nil, jsonContent, nil)
|
||||
}
|
||||
|
|
|
@ -244,6 +244,54 @@ func (h *CreateProjectGroupHandler) ServeHTTP(w http.ResponseWriter, r *http.Req
|
|||
}
|
||||
}
|
||||
|
||||
type UpdateProjectGroupHandler struct {
|
||||
log *zap.SugaredLogger
|
||||
ah *action.ActionHandler
|
||||
readDB *readdb.ReadDB
|
||||
}
|
||||
|
||||
func NewUpdateProjectGroupHandler(logger *zap.Logger, ah *action.ActionHandler, readDB *readdb.ReadDB) *UpdateProjectGroupHandler {
|
||||
return &UpdateProjectGroupHandler{log: logger.Sugar(), ah: ah, readDB: readDB}
|
||||
}
|
||||
|
||||
func (h *UpdateProjectGroupHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
vars := mux.Vars(r)
|
||||
projectGroupRef, err := url.PathUnescape(vars["projectgroupref"])
|
||||
if err != nil {
|
||||
httpError(w, util.NewErrBadRequest(err))
|
||||
return
|
||||
}
|
||||
|
||||
var projectGroup *types.ProjectGroup
|
||||
d := json.NewDecoder(r.Body)
|
||||
if err := d.Decode(&projectGroup); err != nil {
|
||||
httpError(w, util.NewErrBadRequest(err))
|
||||
return
|
||||
}
|
||||
|
||||
areq := &action.UpdateProjectGroupRequest{
|
||||
ProjectGroupRef: projectGroupRef,
|
||||
ProjectGroup: projectGroup,
|
||||
}
|
||||
projectGroup, err = h.ah.UpdateProjectGroup(ctx, areq)
|
||||
if httpError(w, err) {
|
||||
h.log.Errorf("err: %+v", err)
|
||||
return
|
||||
}
|
||||
|
||||
resProjectGroup, err := projectGroupResponse(h.readDB, projectGroup)
|
||||
if httpError(w, err) {
|
||||
h.log.Errorf("err: %+v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := httpResponse(w, http.StatusCreated, resProjectGroup); err != nil {
|
||||
h.log.Errorf("err: %+v", err)
|
||||
}
|
||||
}
|
||||
|
||||
type DeleteProjectGroupHandler struct {
|
||||
log *zap.SugaredLogger
|
||||
ah *action.ActionHandler
|
||||
|
|
|
@ -129,6 +129,7 @@ func (s *Configstore) Run(ctx context.Context) error {
|
|||
projectGroupSubgroupsHandler := api.NewProjectGroupSubgroupsHandler(logger, s.ah, s.readDB)
|
||||
projectGroupProjectsHandler := api.NewProjectGroupProjectsHandler(logger, s.ah, s.readDB)
|
||||
createProjectGroupHandler := api.NewCreateProjectGroupHandler(logger, s.ah, s.readDB)
|
||||
updateProjectGroupHandler := api.NewUpdateProjectGroupHandler(logger, s.ah, s.readDB)
|
||||
deleteProjectGroupHandler := api.NewDeleteProjectGroupHandler(logger, s.ah)
|
||||
|
||||
projectHandler := api.NewProjectHandler(logger, s.readDB)
|
||||
|
@ -180,6 +181,7 @@ func (s *Configstore) Run(ctx context.Context) error {
|
|||
apirouter.Handle("/projectgroups/{projectgroupref}/subgroups", projectGroupSubgroupsHandler).Methods("GET")
|
||||
apirouter.Handle("/projectgroups/{projectgroupref}/projects", projectGroupProjectsHandler).Methods("GET")
|
||||
apirouter.Handle("/projectgroups", createProjectGroupHandler).Methods("POST")
|
||||
apirouter.Handle("/projectgroups/{projectgroupref}", updateProjectGroupHandler).Methods("PUT")
|
||||
apirouter.Handle("/projectgroups/{projectgroupref}", deleteProjectGroupHandler).Methods("DELETE")
|
||||
|
||||
apirouter.Handle("/projects/{projectref}", projectHandler).Methods("GET")
|
||||
|
|
|
@ -104,6 +104,38 @@ func (h *ActionHandler) CreateProjectGroup(ctx context.Context, req *CreateProje
|
|||
return rp, nil
|
||||
}
|
||||
|
||||
type UpdateProjectGroupRequest struct {
|
||||
Name string
|
||||
Visibility types.Visibility
|
||||
}
|
||||
|
||||
func (h *ActionHandler) UpdateProjectGroup(ctx context.Context, projectGroupRef string, req *UpdateProjectGroupRequest) (*csapi.ProjectGroup, error) {
|
||||
pg, resp, err := h.configstoreClient.GetProjectGroup(ctx, projectGroupRef)
|
||||
if err != nil {
|
||||
return nil, ErrFromRemote(resp, errors.Wrapf(err, "failed to get project group %q", projectGroupRef))
|
||||
}
|
||||
|
||||
isProjectOwner, err := h.IsProjectOwner(ctx, pg.OwnerType, pg.OwnerID)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to determine ownership")
|
||||
}
|
||||
if !isProjectOwner {
|
||||
return nil, util.NewErrForbidden(errors.Errorf("user not authorized"))
|
||||
}
|
||||
|
||||
pg.Name = req.Name
|
||||
pg.Visibility = req.Visibility
|
||||
|
||||
h.log.Infof("updating project group")
|
||||
rp, resp, err := h.configstoreClient.UpdateProjectGroup(ctx, pg.ID, pg.ProjectGroup)
|
||||
if err != nil {
|
||||
return nil, ErrFromRemote(resp, errors.Wrapf(err, "failed to update project group"))
|
||||
}
|
||||
h.log.Infof("project group %q updated, ID: %s", pg.Name, pg.ID)
|
||||
|
||||
return rp, nil
|
||||
}
|
||||
|
||||
func (h *ActionHandler) DeleteProjectGroup(ctx context.Context, projectRef string) error {
|
||||
p, resp, err := h.configstoreClient.GetProjectGroup(ctx, projectRef)
|
||||
if err != nil {
|
||||
|
|
|
@ -30,9 +30,9 @@ import (
|
|||
)
|
||||
|
||||
type CreateProjectGroupRequest struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
ParentRef string `json:"parent_ref,omitempty"`
|
||||
Visibility types.Visibility `json:"visibility,omitempty"`
|
||||
Name string `json:"name"`
|
||||
ParentRef string `json:"parent_ref"`
|
||||
Visibility types.Visibility `json:"visibility"`
|
||||
}
|
||||
|
||||
type CreateProjectGroupHandler struct {
|
||||
|
@ -81,6 +81,52 @@ func (h *CreateProjectGroupHandler) ServeHTTP(w http.ResponseWriter, r *http.Req
|
|||
}
|
||||
}
|
||||
|
||||
type UpdateProjectGroupRequest struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Visibility types.Visibility `json:"visibility,omitempty"`
|
||||
}
|
||||
|
||||
type UpdateProjectGroupHandler struct {
|
||||
log *zap.SugaredLogger
|
||||
ah *action.ActionHandler
|
||||
}
|
||||
|
||||
func NewUpdateProjectGroupHandler(logger *zap.Logger, ah *action.ActionHandler) *UpdateProjectGroupHandler {
|
||||
return &UpdateProjectGroupHandler{log: logger.Sugar(), ah: ah}
|
||||
}
|
||||
|
||||
func (h *UpdateProjectGroupHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
vars := mux.Vars(r)
|
||||
projectGroupRef, err := url.PathUnescape(vars["projectgroupref"])
|
||||
if err != nil {
|
||||
httpError(w, util.NewErrBadRequest(err))
|
||||
return
|
||||
}
|
||||
|
||||
var req UpdateProjectGroupRequest
|
||||
d := json.NewDecoder(r.Body)
|
||||
if err := d.Decode(&req); err != nil {
|
||||
httpError(w, util.NewErrBadRequest(err))
|
||||
return
|
||||
}
|
||||
|
||||
areq := &action.UpdateProjectGroupRequest{
|
||||
Name: req.Name,
|
||||
Visibility: req.Visibility,
|
||||
}
|
||||
projectGroup, err := h.ah.UpdateProjectGroup(ctx, projectGroupRef, areq)
|
||||
if httpError(w, err) {
|
||||
h.log.Errorf("err: %+v", err)
|
||||
return
|
||||
}
|
||||
|
||||
res := createProjectGroupResponse(projectGroup)
|
||||
if err := httpResponse(w, http.StatusCreated, res); err != nil {
|
||||
h.log.Errorf("err: %+v", err)
|
||||
}
|
||||
}
|
||||
|
||||
type DeleteProjectGroupHandler struct {
|
||||
log *zap.SugaredLogger
|
||||
ah *action.ActionHandler
|
||||
|
@ -209,12 +255,12 @@ func (h *ProjectGroupSubgroupsHandler) ServeHTTP(w http.ResponseWriter, r *http.
|
|||
}
|
||||
|
||||
type ProjectGroupResponse struct {
|
||||
ID string `json:"id,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Path string `json:"path,omitempty"`
|
||||
ParentPath string `json:"parent_path,omitempty"`
|
||||
Visibility types.Visibility `json:"visibility,omitempty"`
|
||||
GlobalVisibility string `json:"global_visibility,omitempty"`
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Path string `json:"path"`
|
||||
ParentPath string `json:"parent_path"`
|
||||
Visibility types.Visibility `json:"visibility"`
|
||||
GlobalVisibility string `json:"global_visibility"`
|
||||
}
|
||||
|
||||
func createProjectGroupResponse(r *csapi.ProjectGroup) *ProjectGroupResponse {
|
||||
|
|
|
@ -153,6 +153,7 @@ func (g *Gateway) Run(ctx context.Context) error {
|
|||
projectGroupSubgroupsHandler := api.NewProjectGroupSubgroupsHandler(logger, g.ah)
|
||||
projectGroupProjectsHandler := api.NewProjectGroupProjectsHandler(logger, g.ah)
|
||||
createProjectGroupHandler := api.NewCreateProjectGroupHandler(logger, g.ah)
|
||||
updateProjectGroupHandler := api.NewUpdateProjectGroupHandler(logger, g.ah)
|
||||
deleteProjectGroupHandler := api.NewDeleteProjectGroupHandler(logger, g.ah)
|
||||
|
||||
projectHandler := api.NewProjectHandler(logger, g.ah)
|
||||
|
@ -230,6 +231,7 @@ func (g *Gateway) Run(ctx context.Context) error {
|
|||
apirouter.Handle("/projectgroups/{projectgroupref}/subgroups", authForcedHandler(projectGroupSubgroupsHandler)).Methods("GET")
|
||||
apirouter.Handle("/projectgroups/{projectgroupref}/projects", authForcedHandler(projectGroupProjectsHandler)).Methods("GET")
|
||||
apirouter.Handle("/projectgroups", authForcedHandler(createProjectGroupHandler)).Methods("POST")
|
||||
apirouter.Handle("/projectgroups/{projectgroupref}", authForcedHandler(updateProjectGroupHandler)).Methods("PUT")
|
||||
apirouter.Handle("/projectgroups/{projectgroupref}", authForcedHandler(deleteProjectGroupHandler)).Methods("DELETE")
|
||||
|
||||
apirouter.Handle("/projects/{projectref}", authForcedHandler(projectHandler)).Methods("GET")
|
||||
|
|
Loading…
Reference in New Issue