fix(handler): fix batch operations and error handling bugs (#779)

fix(handler): fix batch operations and error handling bugs
This commit is contained in:
Qiaochu Hu
2026-04-12 23:00:40 +08:00
committed by GitHub
parent 0c28d3cd08
commit 0c2102b951
2 changed files with 40 additions and 2 deletions

View File

@@ -412,7 +412,12 @@ func (h *Handler) GoogleLogin(w http.ResponseWriter, r *http.Request) {
}
// Fetch user info from Google.
userInfoReq, _ := http.NewRequestWithContext(r.Context(), http.MethodGet, "https://www.googleapis.com/oauth2/v2/userinfo", nil)
userInfoReq, err := http.NewRequestWithContext(r.Context(), http.MethodGet, "https://www.googleapis.com/oauth2/v2/userinfo", nil)
if err != nil {
slog.Error("failed to create userinfo request", "error", err)
writeError(w, http.StatusInternalServerError, "internal error")
return
}
userInfoReq.Header.Set("Authorization", "Bearer "+gToken.AccessToken)
userInfoResp, err := http.DefaultClient.Do(userInfoReq)

View File

@@ -1252,6 +1252,7 @@ func (h *Handler) BatchUpdateIssues(w http.ResponseWriter, r *http.Request) {
AssigneeID: prevIssue.AssigneeID,
DueDate: prevIssue.DueDate,
ParentIssueID: prevIssue.ParentIssueID,
ProjectID: prevIssue.ProjectID,
}
if req.Updates.Title != nil {
@@ -1295,6 +1296,33 @@ func (h *Handler) BatchUpdateIssues(w http.ResponseWriter, r *http.Request) {
}
}
if _, ok := rawUpdates["parent_issue_id"]; ok {
if req.Updates.ParentIssueID != nil {
newParentID := parseUUID(*req.Updates.ParentIssueID)
// Cannot set self as parent.
if uuidToString(newParentID) == issueID {
continue
}
// Validate parent exists in the same workspace.
if _, err := h.Queries.GetIssueInWorkspace(r.Context(), db.GetIssueInWorkspaceParams{
ID: newParentID,
WorkspaceID: prevIssue.WorkspaceID,
}); err != nil {
continue
}
params.ParentIssueID = newParentID
} else {
params.ParentIssueID = pgtype.UUID{Valid: false}
}
}
if _, ok := rawUpdates["project_id"]; ok {
if req.Updates.ProjectID != nil {
params.ProjectID = parseUUID(*req.Updates.ProjectID)
} else {
params.ProjectID = pgtype.UUID{Valid: false}
}
}
// Enforce agent visibility for batch assignment.
if req.Updates.AssigneeType != nil && *req.Updates.AssigneeType == "agent" && req.Updates.AssigneeID != nil {
if ok, _ := h.canAssignAgent(r.Context(), r, *req.Updates.AssigneeID, workspaceID); !ok {
@@ -1372,11 +1400,16 @@ func (h *Handler) BatchDeleteIssues(w http.ResponseWriter, r *http.Request) {
h.TaskService.CancelTasksForIssue(r.Context(), issue.ID)
if err := h.Queries.DeleteIssue(r.Context(), parseUUID(issueID)); err != nil {
// Collect attachment URLs before CASCADE delete to clean up S3 objects.
attachmentURLs, _ := h.Queries.ListAttachmentURLsByIssueOrComments(r.Context(), issue.ID)
if err := h.Queries.DeleteIssue(r.Context(), issue.ID); err != nil {
slog.Warn("batch delete issue failed", "issue_id", issueID, "error", err)
continue
}
h.deleteS3Objects(r.Context(), attachmentURLs)
actorType, actorID := h.resolveActor(r, userID, workspaceID)
h.publish(protocol.EventIssueDeleted, workspaceID, actorType, actorID, map[string]any{"issue_id": issueID})
deleted++