configstore: add UpdateProject api
This commit is contained in:
parent
79c1a60a36
commit
e1d0318c9b
|
@ -153,6 +153,113 @@ func (h *ActionHandler) CreateProject(ctx context.Context, project *types.Projec
|
||||||
return project, err
|
return project, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type UpdateProjectRequest struct {
|
||||||
|
ProjectRef string
|
||||||
|
|
||||||
|
Project *types.Project
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *ActionHandler) UpdateProject(ctx context.Context, req *UpdateProjectRequest) (*types.Project, error) {
|
||||||
|
if err := h.ValidateProject(ctx, req.Project); 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
|
||||||
|
p, err := h.readDB.GetProject(tx, req.ProjectRef)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if p == nil {
|
||||||
|
return util.NewErrBadRequest(errors.Errorf("project with ref %q doesn't exist", req.ProjectRef))
|
||||||
|
}
|
||||||
|
// check that the project.ID matches
|
||||||
|
if p.ID != req.Project.ID {
|
||||||
|
return util.NewErrBadRequest(errors.Errorf("project with ref %q has a different id", req.ProjectRef))
|
||||||
|
}
|
||||||
|
|
||||||
|
// check parent project group exists
|
||||||
|
group, err := h.readDB.GetProjectGroup(tx, req.Project.Parent.ID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if group == nil {
|
||||||
|
return util.NewErrBadRequest(errors.Errorf("project group with id %q doesn't exist", req.Project.Parent.ID))
|
||||||
|
}
|
||||||
|
|
||||||
|
// currently we don't support changing parent
|
||||||
|
// TODO(sgotti) handle project move (changed parent project group)
|
||||||
|
if p.Parent.ID != req.Project.Parent.ID {
|
||||||
|
return util.NewErrBadRequest(errors.Errorf("changing project parent isn't supported"))
|
||||||
|
}
|
||||||
|
|
||||||
|
pp, err := h.readDB.GetProjectPath(tx, p)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// check duplicate project name
|
||||||
|
ap, err := h.readDB.GetProjectByName(tx, req.Project.Parent.ID, req.Project.Name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if ap != nil {
|
||||||
|
return util.NewErrBadRequest(errors.Errorf("project with name %q, path %q already exists", p.Name, pp))
|
||||||
|
}
|
||||||
|
|
||||||
|
// changegroup is the project path. Use "projectpath" prefix as it must
|
||||||
|
// cover both projects and projectgroups
|
||||||
|
cgNames := []string{util.EncodeSha256Hex("projectpath-" + pp)}
|
||||||
|
cgt, err = h.readDB.GetChangeGroupsUpdateTokens(tx, cgNames)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.Project.RemoteRepositoryConfigType == types.RemoteRepositoryConfigTypeRemoteSource {
|
||||||
|
// check that the linked account matches the remote source
|
||||||
|
user, err := h.readDB.GetUserByLinkedAccount(tx, req.Project.LinkedAccountID)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "failed to get user with linked account id %q", req.Project.LinkedAccountID)
|
||||||
|
}
|
||||||
|
if user == nil {
|
||||||
|
return util.NewErrBadRequest(errors.Errorf("user for linked account %q doesn't exist", req.Project.LinkedAccountID))
|
||||||
|
}
|
||||||
|
la, ok := user.LinkedAccounts[req.Project.LinkedAccountID]
|
||||||
|
if !ok {
|
||||||
|
return util.NewErrBadRequest(errors.Errorf("linked account id %q for user %q doesn't exist", req.Project.LinkedAccountID, user.Name))
|
||||||
|
}
|
||||||
|
if la.RemoteSourceID != req.Project.RemoteSourceID {
|
||||||
|
return util.NewErrBadRequest(errors.Errorf("linked account id %q remote source %q different than project remote source %q", req.Project.LinkedAccountID, la.RemoteSourceID, req.Project.RemoteSourceID))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
pcj, err := json.Marshal(req.Project)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "failed to marshal project")
|
||||||
|
}
|
||||||
|
actions := []*datamanager.Action{
|
||||||
|
{
|
||||||
|
ActionType: datamanager.ActionTypePut,
|
||||||
|
DataType: string(types.ConfigTypeProject),
|
||||||
|
ID: req.Project.ID,
|
||||||
|
Data: pcj,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = h.dm.WriteWal(ctx, actions, cgt)
|
||||||
|
return req.Project, err
|
||||||
|
}
|
||||||
|
|
||||||
func (h *ActionHandler) DeleteProject(ctx context.Context, projectRef string) error {
|
func (h *ActionHandler) DeleteProject(ctx context.Context, projectRef string) error {
|
||||||
var project *types.Project
|
var project *types.Project
|
||||||
|
|
||||||
|
|
|
@ -152,6 +152,17 @@ func (c *Client) CreateProject(ctx context.Context, project *types.Project) (*Pr
|
||||||
return resProject, resp, err
|
return resProject, resp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Client) UpdateProject(ctx context.Context, projectRef string, project *types.Project) (*Project, *http.Response, error) {
|
||||||
|
pj, err := json.Marshal(project)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resProject := new(Project)
|
||||||
|
resp, err := c.getParsedResponse(ctx, "PUT", fmt.Sprintf("/projects/%s", url.PathEscape(projectRef)), nil, jsonContent, bytes.NewReader(pj), resProject)
|
||||||
|
return resProject, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Client) DeleteProject(ctx context.Context, projectRef string) (*http.Response, error) {
|
func (c *Client) DeleteProject(ctx context.Context, projectRef string) (*http.Response, error) {
|
||||||
return c.getResponse(ctx, "DELETE", fmt.Sprintf("/projects/%s", url.PathEscape(projectRef)), nil, jsonContent, nil)
|
return c.getResponse(ctx, "DELETE", fmt.Sprintf("/projects/%s", url.PathEscape(projectRef)), nil, jsonContent, nil)
|
||||||
}
|
}
|
||||||
|
|
|
@ -194,6 +194,54 @@ func (h *CreateProjectHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type UpdateProjectHandler struct {
|
||||||
|
log *zap.SugaredLogger
|
||||||
|
ah *action.ActionHandler
|
||||||
|
readDB *readdb.ReadDB
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewUpdateProjectHandler(logger *zap.Logger, ah *action.ActionHandler, readDB *readdb.ReadDB) *UpdateProjectHandler {
|
||||||
|
return &UpdateProjectHandler{log: logger.Sugar(), ah: ah, readDB: readDB}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *UpdateProjectHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
projectRef, err := url.PathUnescape(vars["projectref"])
|
||||||
|
if err != nil {
|
||||||
|
httpError(w, util.NewErrBadRequest(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var project *types.Project
|
||||||
|
d := json.NewDecoder(r.Body)
|
||||||
|
if err := d.Decode(&project); err != nil {
|
||||||
|
httpError(w, util.NewErrBadRequest(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
areq := &action.UpdateProjectRequest{
|
||||||
|
ProjectRef: projectRef,
|
||||||
|
Project: project,
|
||||||
|
}
|
||||||
|
project, err = h.ah.UpdateProject(ctx, areq)
|
||||||
|
if httpError(w, err) {
|
||||||
|
h.log.Errorf("err: %+v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
resProject, err := projectResponse(h.readDB, project)
|
||||||
|
if httpError(w, err) {
|
||||||
|
h.log.Errorf("err: %+v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := httpResponse(w, http.StatusCreated, resProject); err != nil {
|
||||||
|
h.log.Errorf("err: %+v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type DeleteProjectHandler struct {
|
type DeleteProjectHandler struct {
|
||||||
log *zap.SugaredLogger
|
log *zap.SugaredLogger
|
||||||
ah *action.ActionHandler
|
ah *action.ActionHandler
|
||||||
|
|
|
@ -132,6 +132,7 @@ func (s *Configstore) Run(ctx context.Context) error {
|
||||||
|
|
||||||
projectHandler := api.NewProjectHandler(logger, s.readDB)
|
projectHandler := api.NewProjectHandler(logger, s.readDB)
|
||||||
createProjectHandler := api.NewCreateProjectHandler(logger, s.ah, s.readDB)
|
createProjectHandler := api.NewCreateProjectHandler(logger, s.ah, s.readDB)
|
||||||
|
updateProjectHandler := api.NewUpdateProjectHandler(logger, s.ah, s.readDB)
|
||||||
deleteProjectHandler := api.NewDeleteProjectHandler(logger, s.ah)
|
deleteProjectHandler := api.NewDeleteProjectHandler(logger, s.ah)
|
||||||
|
|
||||||
secretsHandler := api.NewSecretsHandler(logger, s.readDB)
|
secretsHandler := api.NewSecretsHandler(logger, s.readDB)
|
||||||
|
@ -177,6 +178,7 @@ func (s *Configstore) Run(ctx context.Context) error {
|
||||||
|
|
||||||
apirouter.Handle("/projects/{projectref}", projectHandler).Methods("GET")
|
apirouter.Handle("/projects/{projectref}", projectHandler).Methods("GET")
|
||||||
apirouter.Handle("/projects", createProjectHandler).Methods("POST")
|
apirouter.Handle("/projects", createProjectHandler).Methods("POST")
|
||||||
|
apirouter.Handle("/projects/{projectref}", updateProjectHandler).Methods("PUT")
|
||||||
apirouter.Handle("/projects/{projectref}", deleteProjectHandler).Methods("DELETE")
|
apirouter.Handle("/projects/{projectref}", deleteProjectHandler).Methods("DELETE")
|
||||||
|
|
||||||
apirouter.Handle("/projectgroups/{projectgroupref}/secrets", secretsHandler).Methods("GET")
|
apirouter.Handle("/projectgroups/{projectgroupref}/secrets", secretsHandler).Methods("GET")
|
||||||
|
|
Loading…
Reference in New Issue