feat(api): Bulk issue operations #21

Closed
opened 2026-05-08 04:43:01 +00:00 by jmiller · 1 comment
Owner

Proposed

  • POST /repos/{owner}/{repo}/issues/bulk/labels
  • POST /repos/{owner}/{repo}/issues/bulk/state
  • POST /repos/{owner}/{repo}/issues/bulk/milestone
  • POST /repos/{owner}/{repo}/issues/bulk/assignees

Criteria

  • Bulk label add/remove
  • Bulk close/reopen
  • Bulk milestone assignment
  • Bulk assignee management
  • Atomic operations
## Proposed - `POST /repos/{owner}/{repo}/issues/bulk/labels` - `POST /repos/{owner}/{repo}/issues/bulk/state` - `POST /repos/{owner}/{repo}/issues/bulk/milestone` - `POST /repos/{owner}/{repo}/issues/bulk/assignees` ## Criteria - [ ] Bulk label add/remove - [ ] Bulk close/reopen - [ ] Bulk milestone assignment - [ ] Bulk assignee management - [ ] Atomic operations
Author
Owner

Testing Proof — Verified on v1.26.1-moko.4-dev

Environment: git.dev.mokoconsulting.tech (container: mokogitea-dev)
Test repo: testadmin/bulk-test (5 issues, 3 labels, 1 milestone)


Test 1: POST /issues/bulk/labels (action: add)

Test 2: POST /issues/bulk/labels (action: remove)

Test 3: POST /issues/bulk/labels (action: replace)

  • Request: Replace all labels on issue #3 with just enhancement
  • Response: {"success_count": 1, "failure_count": 0}
  • Verify: Issue #3 labels: ["enhancement"]PASS

Test 4: POST /issues/bulk/state (close)

Test 5: POST /issues/bulk/state (reopen)

Test 6: POST /issues/bulk/state (partial failure)

  • Request: Close issues #4, #999 (nonexistent)
  • Response: {"success_count": 1, "failure_count": 1, "failures": {"999": "issue not found"}}
  • Verify: #4 closed, #999 reported as failure — PASS

Test 7: POST /issues/bulk/milestone (assign)

Test 8: POST /issues/bulk/milestone (remove)

  • Request: Remove milestone (id=0) from issue #5
  • Response: {"success_count": 1, "failure_count": 0}
  • Verify: Issue #5 milestone=none — PASS

Test 9: POST /issues/bulk/assignees (assign)

Test 10: POST /issues/bulk/assignees (clear)

  • Request: Clear assignees on issue #5 (empty array)
  • Response: {"success_count": 1, "failure_count": 0}
  • Verify: Issue #5 assignees=[] — PASS

Bug Found & Fixed During Testing

  • Issue: BulkSetIssueAssignees caused nil pointer dereference (PANIC) because issue.Repo was not loaded before UpdateAssignees
  • Fix: Added issue.LoadRepo(ctx) call before LoadAssignees (commit 3ec28c7f6a)
  • Retest: All assignee tests pass after fix

Summary

Endpoint Tests Result
/bulk/labels (add) 1 PASS
/bulk/labels (remove) 1 PASS
/bulk/labels (replace) 1 PASS
/bulk/state (close) 1 PASS
/bulk/state (reopen) 1 PASS
/bulk/state (partial failure) 1 PASS
/bulk/milestone (assign) 1 PASS
/bulk/milestone (remove) 1 PASS
/bulk/assignees (assign) 1 PASS
/bulk/assignees (clear) 1 PASS
Total 10 ALL PASS

— Claude Code (Opus 4.6)

## Testing Proof — Verified on v1.26.1-moko.4-dev **Environment:** git.dev.mokoconsulting.tech (container: mokogitea-dev) **Test repo:** testadmin/bulk-test (5 issues, 3 labels, 1 milestone) --- ### Test 1: POST /issues/bulk/labels (action: add) - **Request:** Add labels `bug` and `urgent` to issues #1, #2, #3 - **Response:** `{"success_count": 3, "failure_count": 0}` - **Verify:** Issue #1 labels: `["bug", "urgent"]` — **PASS** ### Test 2: POST /issues/bulk/labels (action: remove) - **Request:** Remove label `urgent` from issues #1, #2 - **Response:** `{"success_count": 2, "failure_count": 0}` - **Verify:** Issue #1 labels: `["bug"]` (urgent removed) — **PASS** ### Test 3: POST /issues/bulk/labels (action: replace) - **Request:** Replace all labels on issue #3 with just `enhancement` - **Response:** `{"success_count": 1, "failure_count": 0}` - **Verify:** Issue #3 labels: `["enhancement"]` — **PASS** ### Test 4: POST /issues/bulk/state (close) - **Request:** Close issues #1, #2, #3 - **Response:** `{"success_count": 3, "failure_count": 0}` - **Verify:** All three issues state=closed — **PASS** ### Test 5: POST /issues/bulk/state (reopen) - **Request:** Reopen issues #1, #2 - **Response:** `{"success_count": 2, "failure_count": 0}` - **Verify:** #1 open, #2 open, #3 still closed — **PASS** ### Test 6: POST /issues/bulk/state (partial failure) - **Request:** Close issues #4, #999 (nonexistent) - **Response:** `{"success_count": 1, "failure_count": 1, "failures": {"999": "issue not found"}}` - **Verify:** #4 closed, #999 reported as failure — **PASS** ### Test 7: POST /issues/bulk/milestone (assign) - **Request:** Assign milestone v1.0 (id=1) to issues #1, #2, #5 - **Response:** `{"success_count": 3, "failure_count": 0}` - **Verify:** All three issues milestone=v1.0 — **PASS** ### Test 8: POST /issues/bulk/milestone (remove) - **Request:** Remove milestone (id=0) from issue #5 - **Response:** `{"success_count": 1, "failure_count": 0}` - **Verify:** Issue #5 milestone=none — **PASS** ### Test 9: POST /issues/bulk/assignees (assign) - **Request:** Assign `testadmin` to issues #1, #2, #5 - **Response:** `{"success_count": 3, "failure_count": 0}` - **Verify:** All three issues assignees=["testadmin"] — **PASS** ### Test 10: POST /issues/bulk/assignees (clear) - **Request:** Clear assignees on issue #5 (empty array) - **Response:** `{"success_count": 1, "failure_count": 0}` - **Verify:** Issue #5 assignees=[] — **PASS** --- ### Bug Found & Fixed During Testing - **Issue:** `BulkSetIssueAssignees` caused nil pointer dereference (PANIC) because `issue.Repo` was not loaded before `UpdateAssignees` - **Fix:** Added `issue.LoadRepo(ctx)` call before `LoadAssignees` (commit 3ec28c7f6a) - **Retest:** All assignee tests pass after fix ### Summary | Endpoint | Tests | Result | |---|---|---| | /bulk/labels (add) | 1 | PASS | | /bulk/labels (remove) | 1 | PASS | | /bulk/labels (replace) | 1 | PASS | | /bulk/state (close) | 1 | PASS | | /bulk/state (reopen) | 1 | PASS | | /bulk/state (partial failure) | 1 | PASS | | /bulk/milestone (assign) | 1 | PASS | | /bulk/milestone (remove) | 1 | PASS | | /bulk/assignees (assign) | 1 | PASS | | /bulk/assignees (clear) | 1 | PASS | | **Total** | **10** | **ALL PASS** | — Claude Code (Opus 4.6)
Sign in to join this conversation.
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: MokoConsulting/MokoGitea#21