Fix review feedback bugs

- EditProject: wrap field updates and ChangeProjectStatus in db.WithTx so
  a status-change failure doesn't leave a partially applied PATCH.
- Validate EditProjectOption.State against open/closed; 422 on other
  values instead of silently treating them as open.
- Align missing-issue status to 404 (the URL targets a missing resource);
  update existing test that was asserting the old 422.
- RemoveIssueFromProjectColumn: verify the project_issue row matches the
  URL column before clearing the issue's project assignment, since
  IssueAssignOrRemoveProject(projectID=0) detaches the issue from any
  project regardless of column. Returns 404 if the issue isn't in this
  column. New test covers the cross-column case.

Co-Authored-By: Claude (Opus 4.7) <noreply@anthropic.com>
This commit is contained in:
silverwind
2026-05-03 14:32:38 +05:30
committed by beardev-in
co-authored by Claude
parent 438f367ab3
commit efe43882d5
3 changed files with 74 additions and 23 deletions
+28 -4
View File
@@ -277,7 +277,15 @@ func EditProject(ctx *context.APIContext) {
opts.CardType = optional.Some(project_model.CardType(*form.CardType))
}
if form.State != nil {
opts.IsClosed = optional.Some(*form.State == string(api.StateClosed))
switch api.StateType(*form.State) {
case api.StateOpen:
opts.IsClosed = optional.Some(false)
case api.StateClosed:
opts.IsClosed = optional.Some(true)
default:
ctx.APIError(http.StatusUnprocessableEntity, "state must be 'open' or 'closed'")
return
}
}
if err := project_service.UpdateProject(ctx, project, opts); err != nil {
ctx.APIErrorInternal(err)
@@ -765,9 +773,25 @@ func assignIssueToProjectColumn(ctx *context.APIContext, add bool) {
return
}
projectID := int64(0)
if add {
projectID = column.ProjectID
projectID := column.ProjectID
if !add {
// Confirm the issue is currently in this specific column before removing,
// since IssueAssignOrRemoveProject(projectID=0) clears the issue's project
// assignment unconditionally.
exists, err := db.GetEngine(ctx).Exist(&project_model.ProjectIssue{
IssueID: issue.ID,
ProjectID: column.ProjectID,
ProjectColumnID: column.ID,
})
if err != nil {
ctx.APIErrorInternal(err)
return
}
if !exists {
ctx.APIErrorNotFound()
return
}
projectID = 0
}
if err := issues_model.IssueAssignOrRemoveProject(ctx, issue, ctx.Doer, projectID, column.ID); err != nil {
ctx.APIErrorInternal(err)