Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,7 @@ linters:
- HookResponse.RawPayload
- Issue.PullRequestLinks # TODO: PullRequest
- IssueImportRequest.IssueImport # TODO: Issue
- IssueListByRepoOptions.ExcludeLabels # url:"-" skips query param; no matching tag
- IssuesSearchResult.Issues # TODO: Items
- IssuesSearchResult.Total
- LabelsSearchResult.Labels # TODO: Items
Expand Down
8 changes: 8 additions & 0 deletions github/github-accessors.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 11 additions & 0 deletions github/github-accessors_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

30 changes: 30 additions & 0 deletions github/issues.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ package github
import (
"context"
"fmt"
"strings"
"time"
)

Expand Down Expand Up @@ -364,6 +365,11 @@ type IssueListByRepoOptions struct {
// Labels filters issues based on their label.
Labels []string `url:"labels,omitempty,comma"`

// ExcludeLabels filters issues to exclude those with the specified labels.
// Filtering is done client-side after fetching, since GitHub's Issues REST API
// does not support server-side label exclusion.
ExcludeLabels []string `url:"-"`

// Sort specifies how to sort issues. Possible values are: created, updated,
// and comments. Default value is "created".
Sort string `url:"sort,omitempty"`
Expand Down Expand Up @@ -408,6 +414,30 @@ func (s *IssuesService) ListByRepo(ctx context.Context, owner, repo string, opts
return nil, resp, err
}

// Filter out issues with excluded labels client-side.
// The GitHub Issues REST API does not support server-side label exclusion,
// so we apply the filter in-memory after fetching results.
if len(opts.ExcludeLabels) > 0 {
exclude := make(map[string]bool, len(opts.ExcludeLabels))
for _, l := range opts.ExcludeLabels {
exclude[strings.ToLower(l)] = true
}
filtered := make([]*Issue, 0, len(issues))
for _, issue := range issues {
shouldExclude := false
for _, label := range issue.Labels {
if exclude[strings.ToLower(label.GetName())] {
shouldExclude = true
break
}
}
if !shouldExclude {
filtered = append(filtered, issue)
}
}
issues = filtered
}

return issues, resp, nil
}

Expand Down
46 changes: 44 additions & 2 deletions github/issues_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -192,8 +192,8 @@
"assignee": "a",
"creator": "c",
"mentioned": "m",
"labels": "a,b",
"sort": "updated",
"labels": "a,b",

Check failure on line 195 in github/issues_test.go

View workflow job for this annotation

GitHub Actions / golangci-lint

File is not properly formatted (gci)
"sort": "updated",
"direction": "asc",
"since": referenceTime.Format(time.RFC3339),
"per_page": "1",
Expand All @@ -209,6 +209,7 @@
Creator: "c",
Mentioned: "m",
Labels: []string{"a", "b"},
ExcludeLabels: []string{"c", "d"},
Sort: "updated",
Direction: "asc",
Since: referenceTime,
Expand Down Expand Up @@ -241,6 +242,47 @@
})
}

func TestIssuesService_ListByRepo_ExcludeLabels(t *testing.T) {
t.Parallel()
client, mux, _ := setup(t)

mux.HandleFunc("/repos/o/r/issues", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
testHeader(t, r, "Accept", mediaTypeReactionsPreview)
// ExcludeLabels should NOT be sent as a query parameter.
testFormValues(t, r, values{
"labels": "bug",
})
// Server returns all bug-labeled issues; client-side filtering handles exclusion.
fmt.Fprint(w, `[
{"number":1, "labels":[{"name":"bug"}]},
{"number":2, "labels":[{"name":"bug"},{"name":"wontfix"}]},
{"number":3, "labels":[{"name":"bug"},{"name":"enhancement"}]},
{"number":4, "labels":[{"name":"bug"},{"name":"wontfix"},{"name":"duplicate"}]}
]`)
})

opt := &IssueListByRepoOptions{
Labels: []string{"bug"},
ExcludeLabels: []string{"wontfix", "duplicate"},
}

ctx := t.Context()
issues, _, err := client.Issues.ListByRepo(ctx, "o", "r", opt)
if err != nil {
t.Errorf("Issues.ListByRepo returned error: %v", err)
}

// Issues #1 and #3 should remain; #2 and #4 should be excluded by client-side filter.
want := []*Issue{
{Number: Ptr(1), Labels: []*Label{{Name: Ptr("bug")}}},
{Number: Ptr(3), Labels: []*Label{{Name: Ptr("bug")}, {Name: Ptr("enhancement")}}},
}
if !cmp.Equal(issues, want) {
t.Errorf("Issues.ListByRepo returned %+v, want %+v", issues, want)
}
}

func TestIssuesService_Get(t *testing.T) {
t.Parallel()
client, mux, _ := setup(t)
Expand Down
Loading