fix(issue): inherit parent project for sub-issues

This commit is contained in:
Jiayuan Zhang
2026-04-14 01:30:40 +08:00
parent 4bc9969765
commit 56c38dc521
2 changed files with 75 additions and 1 deletions

View File

@@ -302,6 +302,73 @@ func TestCreateIssueExplicitBacklogPreserved(t *testing.T) {
testHandler.DeleteIssue(httptest.NewRecorder(), cleanupReq)
}
func TestCreateSubIssueInheritsParentProject(t *testing.T) {
var projectID, parentID, childID string
defer func() {
for _, issueID := range []string{childID, parentID} {
if issueID == "" {
continue
}
req := newRequest("DELETE", "/api/issues/"+issueID, nil)
req = withURLParam(req, "id", issueID)
testHandler.DeleteIssue(httptest.NewRecorder(), req)
}
if projectID != "" {
req := newRequest("DELETE", "/api/projects/"+projectID, nil)
req = withURLParam(req, "id", projectID)
testHandler.DeleteProject(httptest.NewRecorder(), req)
}
}()
w := httptest.NewRecorder()
req := newRequest("POST", "/api/projects?workspace_id="+testWorkspaceID, map[string]any{
"title": "Sub-issue inheritance project",
})
testHandler.CreateProject(w, req)
if w.Code != http.StatusCreated {
t.Fatalf("CreateProject: expected 201, got %d: %s", w.Code, w.Body.String())
}
var project ProjectResponse
json.NewDecoder(w.Body).Decode(&project)
projectID = project.ID
w = httptest.NewRecorder()
req = newRequest("POST", "/api/issues?workspace_id="+testWorkspaceID, map[string]any{
"title": "Parent with project",
"project_id": projectID,
})
testHandler.CreateIssue(w, req)
if w.Code != http.StatusCreated {
t.Fatalf("CreateIssue parent: expected 201, got %d: %s", w.Code, w.Body.String())
}
var parent IssueResponse
json.NewDecoder(w.Body).Decode(&parent)
parentID = parent.ID
if parent.ProjectID == nil || *parent.ProjectID != projectID {
t.Fatalf("CreateIssue parent: expected project_id %q, got %v", projectID, parent.ProjectID)
}
w = httptest.NewRecorder()
req = newRequest("POST", "/api/issues?workspace_id="+testWorkspaceID, map[string]any{
"title": "Child without explicit project",
"parent_issue_id": parentID,
})
testHandler.CreateIssue(w, req)
if w.Code != http.StatusCreated {
t.Fatalf("CreateIssue child: expected 201, got %d: %s", w.Code, w.Body.String())
}
var child IssueResponse
json.NewDecoder(w.Body).Decode(&child)
childID = child.ID
if child.ParentIssueID == nil || *child.ParentIssueID != parentID {
t.Fatalf("CreateIssue child: expected parent_issue_id %q, got %v", parentID, child.ParentIssueID)
}
if child.ProjectID == nil || *child.ProjectID != projectID {
t.Fatalf("CreateIssue child: expected inherited project_id %q, got %v", projectID, child.ProjectID)
}
}
func TestCommentCRUD(t *testing.T) {
// Create an issue first
w := httptest.NewRecorder()

View File

@@ -815,6 +815,10 @@ func (h *Handler) CreateIssue(w http.ResponseWriter, r *http.Request) {
}
var parentIssueID pgtype.UUID
var projectID pgtype.UUID
if req.ProjectID != nil {
projectID = parseUUID(*req.ProjectID)
}
if req.ParentIssueID != nil {
parentIssueID = parseUUID(*req.ParentIssueID)
// Validate parent exists in the same workspace.
@@ -826,6 +830,9 @@ func (h *Handler) CreateIssue(w http.ResponseWriter, r *http.Request) {
writeError(w, http.StatusBadRequest, "parent issue not found in this workspace")
return
}
if req.ProjectID == nil {
projectID = parent.ProjectID
}
}
var dueDate pgtype.Timestamptz
@@ -872,7 +879,7 @@ func (h *Handler) CreateIssue(w http.ResponseWriter, r *http.Request) {
Position: 0,
DueDate: dueDate,
Number: issueNumber,
ProjectID: func() pgtype.UUID { if req.ProjectID != nil { return parseUUID(*req.ProjectID) }; return pgtype.UUID{} }(),
ProjectID: projectID,
})
if err != nil {
slog.Warn("create issue failed", append(logger.RequestAttrs(r), "error", err, "workspace_id", workspaceID)...)