Skip to content

Commit cc0e642

Browse files
authored
feat: Add support for sub-issue (#3580)
1 parent 304d3d0 commit cc0e642

File tree

6 files changed

+373
-0
lines changed

6 files changed

+373
-0
lines changed

github/github-accessors.go

Lines changed: 24 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

github/github-accessors_test.go

Lines changed: 33 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

github/github.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,7 @@ type Client struct {
222222
Search *SearchService
223223
SecretScanning *SecretScanningService
224224
SecurityAdvisories *SecurityAdvisoriesService
225+
SubIssue *SubIssueService
225226
Teams *TeamsService
226227
Users *UsersService
227228
}
@@ -458,6 +459,7 @@ func (c *Client) initialize() {
458459
c.Search = (*SearchService)(&c.common)
459460
c.SecretScanning = (*SecretScanningService)(&c.common)
460461
c.SecurityAdvisories = (*SecurityAdvisoriesService)(&c.common)
462+
c.SubIssue = (*SubIssueService)(&c.common)
461463
c.Teams = (*TeamsService)(&c.common)
462464
c.Users = (*UsersService)(&c.common)
463465
}

github/strings_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ func TestString(t *testing.T) {
107107
{Hook{ID: Ptr(int64(1))}, `github.Hook{ID:1}`},
108108
{IssueComment{ID: Ptr(int64(1))}, `github.IssueComment{ID:1}`},
109109
{Issue{Number: Ptr(1)}, `github.Issue{Number:1}`},
110+
{SubIssue{ID: Ptr(int64(1))}, `github.SubIssue{ID:1}`},
110111
{Key{ID: Ptr(int64(1))}, `github.Key{ID:1}`},
111112
{Label{ID: Ptr(int64(1)), Name: Ptr("l")}, `github.Label{ID:1, Name:"l"}`},
112113
{Organization{ID: Ptr(int64(1))}, `github.Organization{ID:1}`},

github/sub_issue.go

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
// Copyright 2025 The go-github AUTHORS. All rights reserved.
2+
//
3+
// Use of this source code is governed by a BSD-style
4+
// license that can be found in the LICENSE file.
5+
6+
package github
7+
8+
import (
9+
"context"
10+
"fmt"
11+
)
12+
13+
// SubIssueService handles communication with the sub-issue related
14+
// methods of the GitHub API.
15+
//
16+
// Sub-issues help you group and manage your issues with a parent/child relationship.
17+
//
18+
// GitHub API docs: https://docs.github.com/rest/issues/sub-issues
19+
type SubIssueService service
20+
21+
// SubIssue represents a GitHub sub-issue on a repository.
22+
// Note: As far as the GitHub API is concerned, every pull request is an issue,
23+
// but not every issue is a pull request. Some endpoints, events, and webhooks
24+
// may also return pull requests via this struct. If PullRequestLinks is nil,
25+
// this is an issue, and if PullRequestLinks is not nil, this is a pull request.
26+
// The IsPullRequest helper method can be used to check that.
27+
type SubIssue Issue
28+
29+
func (i SubIssue) String() string {
30+
return Stringify(i)
31+
}
32+
33+
// SubIssueListByIssueOptions specifies the optional parameters to the
34+
// SubIssueService.ListByIssue method.
35+
type SubIssueListByIssueOptions struct {
36+
IssueListByRepoOptions
37+
}
38+
39+
// SubIssueRequest represents a request to add, remove, or reprioritize sub-issues.
40+
type SubIssueRequest struct {
41+
SubIssueID int64 `json:"sub_issue_id"` // Required: The ID of the sub-issue
42+
AfterID *int64 `json:"after_id,omitempty"` // Optional: Position after this sub-issue ID
43+
BeforeID *int64 `json:"before_id,omitempty"` // Optional: Position before this sub-issue ID
44+
ReplaceParent *bool `json:"replace_parent,omitempty"` // Optional: Whether to replace the existing parent
45+
}
46+
47+
// Remove a sub-issue from the specified repository.
48+
//
49+
// GitHub API docs: https://docs.github.com/rest/issues/sub-issues#remove-sub-issue
50+
//
51+
//meta:operation DELETE /repos/{owner}/{repo}/issues/{issue_number}/sub_issue
52+
func (s *SubIssueService) Remove(ctx context.Context, owner, repo string, subIssueNumber int64, subIssue SubIssueRequest) (*SubIssue, *Response, error) {
53+
u := fmt.Sprintf("repos/%v/%v/issues/%v/sub_issues", owner, repo, subIssueNumber)
54+
55+
req, err := s.client.NewRequest("DELETE", u, subIssue)
56+
if err != nil {
57+
return nil, nil, err
58+
}
59+
60+
si := new(SubIssue)
61+
resp, err := s.client.Do(ctx, req, si)
62+
if err != nil {
63+
return nil, resp, err
64+
}
65+
66+
return si, resp, nil
67+
}
68+
69+
// ListByIssue lists all sub-issues for the specified issue.
70+
//
71+
// GitHub API docs: https://docs.github.com/rest/issues/sub-issues#list-sub-issues
72+
//
73+
//meta:operation GET /repos/{owner}/{repo}/issues/{issue_number}/sub_issues
74+
func (s *SubIssueService) ListByIssue(ctx context.Context, owner, repo string, issueNumber int64, opts *IssueListOptions) ([]*SubIssue, *Response, error) {
75+
u := fmt.Sprintf("repos/%v/%v/issues/%v/sub_issues", owner, repo, issueNumber)
76+
u, err := addOptions(u, opts)
77+
if err != nil {
78+
return nil, nil, err
79+
}
80+
81+
req, err := s.client.NewRequest("GET", u, nil)
82+
if err != nil {
83+
return nil, nil, err
84+
}
85+
86+
var subIssues []*SubIssue
87+
resp, err := s.client.Do(ctx, req, &subIssues)
88+
if err != nil {
89+
return nil, resp, err
90+
}
91+
92+
return subIssues, resp, nil
93+
}
94+
95+
// Add adds a sub-issue to the specified issue.
96+
//
97+
// The sub-issue to be added must belong to the same repository owner as the parent issue.
98+
// To replace the existing parent of a sub-issue, set replaceParent to true.
99+
//
100+
// GitHub API docs: https://docs.github.com/rest/issues/sub-issues#add-sub-issue
101+
//
102+
//meta:operation POST /repos/{owner}/{repo}/issues/{issue_number}/sub_issues
103+
func (s *SubIssueService) Add(ctx context.Context, owner, repo string, issueNumber int64, subIssue SubIssueRequest) (*SubIssue, *Response, error) {
104+
u := fmt.Sprintf("repos/%v/%v/issues/%v/sub_issues", owner, repo, issueNumber)
105+
req, err := s.client.NewRequest("POST", u, subIssue)
106+
if err != nil {
107+
return nil, nil, err
108+
}
109+
110+
si := new(SubIssue)
111+
resp, err := s.client.Do(ctx, req, si)
112+
if err != nil {
113+
return nil, resp, err
114+
}
115+
116+
return si, resp, nil
117+
}
118+
119+
// Reprioritize changes a sub-issue's priority to a different position in the parent list.
120+
//
121+
// Either afterId or beforeId must be specified to determine the new position of the sub-issue.
122+
//
123+
// GitHub API docs: https://docs.github.com/rest/issues/sub-issues#reprioritize-sub-issue
124+
//
125+
//meta:operation PATCH /repos/{owner}/{repo}/issues/{issue_number}/sub_issues/priority
126+
func (s *SubIssueService) Reprioritize(ctx context.Context, owner, repo string, issueNumber int64, subIssue SubIssueRequest) (*SubIssue, *Response, error) {
127+
u := fmt.Sprintf("repos/%v/%v/issues/%v/sub_issues/priority", owner, repo, issueNumber)
128+
req, err := s.client.NewRequest("PATCH", u, subIssue)
129+
if err != nil {
130+
return nil, nil, err
131+
}
132+
133+
si := new(SubIssue)
134+
resp, err := s.client.Do(ctx, req, si)
135+
if err != nil {
136+
return nil, resp, err
137+
}
138+
139+
return si, resp, nil
140+
}

0 commit comments

Comments
 (0)