From 9c280488e2ac0dddf2533240866ab09c65c5c2f2 Mon Sep 17 00:00:00 2001 From: Colby Williams Date: Mon, 24 Mar 2025 23:53:04 -0500 Subject: [PATCH 1/3] add support for listing reactions by content --- github/reactions.go | 17 ++++++++--------- github/reactions_test.go | 32 +++++++++++++++++++++----------- 2 files changed, 29 insertions(+), 20 deletions(-) diff --git a/github/reactions.go b/github/reactions.go index e9823220372..71dd611d85d 100644 --- a/github/reactions.go +++ b/github/reactions.go @@ -48,9 +48,8 @@ func (r Reaction) String() string { return Stringify(r) } -// ListCommentReactionOptions specifies the optional parameters to the -// ReactionsService.ListCommentReactions method. -type ListCommentReactionOptions struct { +// ListReactionOptions specifies the optional parameters to the list reactions endpoints. +type ListReactionOptions struct { // Content restricts the returned comment reactions to only those with the given type. // Omit this parameter to list all reactions to a commit comment. // Possible values are: "+1", "-1", "laugh", "confused", "heart", "hooray", "rocket", or "eyes". @@ -64,7 +63,7 @@ type ListCommentReactionOptions struct { // GitHub API docs: https://docs.github.com/rest/reactions/reactions#list-reactions-for-a-commit-comment // //meta:operation GET /repos/{owner}/{repo}/comments/{comment_id}/reactions -func (s *ReactionsService) ListCommentReactions(ctx context.Context, owner, repo string, id int64, opts *ListCommentReactionOptions) ([]*Reaction, *Response, error) { +func (s *ReactionsService) ListCommentReactions(ctx context.Context, owner, repo string, id int64, opts *ListReactionOptions) ([]*Reaction, *Response, error) { u := fmt.Sprintf("repos/%v/%v/comments/%v/reactions", owner, repo, id) u, err := addOptions(u, opts) if err != nil { @@ -144,7 +143,7 @@ func (s *ReactionsService) DeleteCommentReactionByID(ctx context.Context, repoID // GitHub API docs: https://docs.github.com/rest/reactions/reactions#list-reactions-for-an-issue // //meta:operation GET /repos/{owner}/{repo}/issues/{issue_number}/reactions -func (s *ReactionsService) ListIssueReactions(ctx context.Context, owner, repo string, number int, opts *ListOptions) ([]*Reaction, *Response, error) { +func (s *ReactionsService) ListIssueReactions(ctx context.Context, owner, repo string, number int, opts *ListReactionOptions) ([]*Reaction, *Response, error) { u := fmt.Sprintf("repos/%v/%v/issues/%v/reactions", owner, repo, number) u, err := addOptions(u, opts) if err != nil { @@ -224,7 +223,7 @@ func (s *ReactionsService) DeleteIssueReactionByID(ctx context.Context, repoID, // GitHub API docs: https://docs.github.com/rest/reactions/reactions#list-reactions-for-an-issue-comment // //meta:operation GET /repos/{owner}/{repo}/issues/comments/{comment_id}/reactions -func (s *ReactionsService) ListIssueCommentReactions(ctx context.Context, owner, repo string, id int64, opts *ListOptions) ([]*Reaction, *Response, error) { +func (s *ReactionsService) ListIssueCommentReactions(ctx context.Context, owner, repo string, id int64, opts *ListReactionOptions) ([]*Reaction, *Response, error) { u := fmt.Sprintf("repos/%v/%v/issues/comments/%v/reactions", owner, repo, id) u, err := addOptions(u, opts) if err != nil { @@ -304,7 +303,7 @@ func (s *ReactionsService) DeleteIssueCommentReactionByID(ctx context.Context, r // GitHub API docs: https://docs.github.com/rest/reactions/reactions#list-reactions-for-a-pull-request-review-comment // //meta:operation GET /repos/{owner}/{repo}/pulls/comments/{comment_id}/reactions -func (s *ReactionsService) ListPullRequestCommentReactions(ctx context.Context, owner, repo string, id int64, opts *ListOptions) ([]*Reaction, *Response, error) { +func (s *ReactionsService) ListPullRequestCommentReactions(ctx context.Context, owner, repo string, id int64, opts *ListReactionOptions) ([]*Reaction, *Response, error) { u := fmt.Sprintf("repos/%v/%v/pulls/comments/%v/reactions", owner, repo, id) u, err := addOptions(u, opts) if err != nil { @@ -384,7 +383,7 @@ func (s *ReactionsService) DeletePullRequestCommentReactionByID(ctx context.Cont // GitHub API docs: https://docs.github.com/rest/reactions/reactions#list-reactions-for-a-team-discussion-legacy // //meta:operation GET /teams/{team_id}/discussions/{discussion_number}/reactions -func (s *ReactionsService) ListTeamDiscussionReactions(ctx context.Context, teamID int64, discussionNumber int, opts *ListOptions) ([]*Reaction, *Response, error) { +func (s *ReactionsService) ListTeamDiscussionReactions(ctx context.Context, teamID int64, discussionNumber int, opts *ListReactionOptions) ([]*Reaction, *Response, error) { u := fmt.Sprintf("teams/%v/discussions/%v/reactions", teamID, discussionNumber) u, err := addOptions(u, opts) if err != nil { @@ -460,7 +459,7 @@ func (s *ReactionsService) DeleteTeamDiscussionReactionByOrgIDAndTeamID(ctx cont // GitHub API docs: https://docs.github.com/rest/reactions/reactions#list-reactions-for-a-team-discussion-comment-legacy // //meta:operation GET /teams/{team_id}/discussions/{discussion_number}/comments/{comment_number}/reactions -func (s *ReactionsService) ListTeamDiscussionCommentReactions(ctx context.Context, teamID int64, discussionNumber, commentNumber int, opts *ListOptions) ([]*Reaction, *Response, error) { +func (s *ReactionsService) ListTeamDiscussionCommentReactions(ctx context.Context, teamID int64, discussionNumber, commentNumber int, opts *ListReactionOptions) ([]*Reaction, *Response, error) { u := fmt.Sprintf("teams/%v/discussions/%v/comments/%v/reactions", teamID, discussionNumber, commentNumber) u, err := addOptions(u, opts) if err != nil { diff --git a/github/reactions_test.go b/github/reactions_test.go index 5758a705f05..71e8efc0a8e 100644 --- a/github/reactions_test.go +++ b/github/reactions_test.go @@ -79,7 +79,7 @@ func TestReactionsService_ListCommentReactions(t *testing.T) { fmt.Fprint(w, `[{"id":1,"user":{"login":"l","id":2},"content":"+1"}]`) }) - opt := &ListCommentReactionOptions{Content: "+1"} + opt := &ListReactionOptions{Content: "+1"} ctx := context.Background() reactions, _, err := client.Reactions.ListCommentReactions(ctx, "o", "r", 1, opt) if err != nil { @@ -149,13 +149,15 @@ func TestReactionsService_ListIssueReactions(t *testing.T) { mux.HandleFunc("/repos/o/r/issues/1/reactions", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") testHeader(t, r, "Accept", mediaTypeReactionsPreview) + testFormValues(t, r, values{"content": "+1"}) w.WriteHeader(http.StatusOK) assertWrite(t, w, []byte(`[{"id":1,"user":{"login":"l","id":2},"content":"+1"}]`)) }) + opt := &ListReactionOptions{Content: "+1"} ctx := context.Background() - got, _, err := client.Reactions.ListIssueReactions(ctx, "o", "r", 1, nil) + got, _, err := client.Reactions.ListIssueReactions(ctx, "o", "r", 1, opt) if err != nil { t.Errorf("ListIssueReactions returned error: %v", err) } @@ -173,7 +175,7 @@ func TestReactionsService_ListIssueReactions_coverage(t *testing.T) { const methodName = "ListIssueReactions" testBadOptions(t, methodName, func() (err error) { - _, _, err = client.Reactions.ListIssueReactions(ctx, "\n", "\n", -1, &ListOptions{}) + _, _, err = client.Reactions.ListIssueReactions(ctx, "\n", "\n", -1, &ListReactionOptions{}) return err }) @@ -230,13 +232,15 @@ func TestReactionsService_ListIssueCommentReactions(t *testing.T) { mux.HandleFunc("/repos/o/r/issues/comments/1/reactions", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") testHeader(t, r, "Accept", mediaTypeReactionsPreview) + testFormValues(t, r, values{"content": "+1"}) w.WriteHeader(http.StatusOK) assertWrite(t, w, []byte(`[{"id":1,"user":{"login":"l","id":2},"content":"+1"}]`)) }) + opt := &ListReactionOptions{Content: "+1"} ctx := context.Background() - got, _, err := client.Reactions.ListIssueCommentReactions(ctx, "o", "r", 1, nil) + got, _, err := client.Reactions.ListIssueCommentReactions(ctx, "o", "r", 1, opt) if err != nil { t.Errorf("ListIssueCommentReactions returned error: %v", err) } @@ -254,7 +258,7 @@ func TestReactionsService_ListIssueCommentReactions_coverage(t *testing.T) { const methodName = "ListIssueCommentReactions" testBadOptions(t, methodName, func() (err error) { - _, _, err = client.Reactions.ListIssueCommentReactions(ctx, "\n", "\n", -1, &ListOptions{}) + _, _, err = client.Reactions.ListIssueCommentReactions(ctx, "\n", "\n", -1, &ListReactionOptions{}) return err }) @@ -311,13 +315,15 @@ func TestReactionsService_ListPullRequestCommentReactions(t *testing.T) { mux.HandleFunc("/repos/o/r/pulls/comments/1/reactions", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") testHeader(t, r, "Accept", mediaTypeReactionsPreview) + testFormValues(t, r, values{"content": "+1"}) w.WriteHeader(http.StatusOK) assertWrite(t, w, []byte(`[{"id":1,"user":{"login":"l","id":2},"content":"+1"}]`)) }) + opt := &ListReactionOptions{Content: "+1"} ctx := context.Background() - got, _, err := client.Reactions.ListPullRequestCommentReactions(ctx, "o", "r", 1, nil) + got, _, err := client.Reactions.ListPullRequestCommentReactions(ctx, "o", "r", 1, opt) if err != nil { t.Errorf("ListPullRequestCommentReactions returned error: %v", err) } @@ -335,7 +341,7 @@ func TestReactionsService_ListPullRequestCommentReactions_coverage(t *testing.T) const methodName = "ListPullRequestCommentReactions" testBadOptions(t, methodName, func() (err error) { - _, _, err = client.Reactions.ListPullRequestCommentReactions(ctx, "\n", "\n", -1, &ListOptions{}) + _, _, err = client.Reactions.ListPullRequestCommentReactions(ctx, "\n", "\n", -1, &ListReactionOptions{}) return err }) @@ -392,13 +398,15 @@ func TestReactionsService_ListTeamDiscussionReactions(t *testing.T) { mux.HandleFunc("/teams/1/discussions/2/reactions", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") testHeader(t, r, "Accept", mediaTypeReactionsPreview) + testFormValues(t, r, values{"content": "+1"}) w.WriteHeader(http.StatusOK) assertWrite(t, w, []byte(`[{"id":1,"user":{"login":"l","id":2},"content":"+1"}]`)) }) + opt := &ListReactionOptions{Content: "+1"} ctx := context.Background() - got, _, err := client.Reactions.ListTeamDiscussionReactions(ctx, 1, 2, nil) + got, _, err := client.Reactions.ListTeamDiscussionReactions(ctx, 1, 2, opt) if err != nil { t.Errorf("ListTeamDiscussionReactions returned error: %v", err) } @@ -416,7 +424,7 @@ func TestReactionsService_ListTeamDiscussionReactions_coverage(t *testing.T) { const methodName = "ListTeamDiscussionReactions" testBadOptions(t, methodName, func() (err error) { - _, _, err = client.Reactions.ListTeamDiscussionReactions(ctx, -1, -2, &ListOptions{}) + _, _, err = client.Reactions.ListTeamDiscussionReactions(ctx, -1, -2, &ListReactionOptions{}) return err }) @@ -473,13 +481,15 @@ func TestReactionService_ListTeamDiscussionCommentReactions(t *testing.T) { mux.HandleFunc("/teams/1/discussions/2/comments/3/reactions", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") testHeader(t, r, "Accept", mediaTypeReactionsPreview) + testFormValues(t, r, values{"content": "+1"}) w.WriteHeader(http.StatusOK) assertWrite(t, w, []byte(`[{"id":1,"user":{"login":"l","id":2},"content":"+1"}]`)) }) + opt := &ListReactionOptions{Content: "+1"} ctx := context.Background() - got, _, err := client.Reactions.ListTeamDiscussionCommentReactions(ctx, 1, 2, 3, nil) + got, _, err := client.Reactions.ListTeamDiscussionCommentReactions(ctx, 1, 2, 3, opt) if err != nil { t.Errorf("ListTeamDiscussionCommentReactions returned error: %v", err) } @@ -497,7 +507,7 @@ func TestReactionService_ListTeamDiscussionCommentReactions_coverage(t *testing. const methodName = "ListTeamDiscussionCommentReactions" testBadOptions(t, methodName, func() (err error) { - _, _, err = client.Reactions.ListTeamDiscussionCommentReactions(ctx, -1, -2, -3, &ListOptions{}) + _, _, err = client.Reactions.ListTeamDiscussionCommentReactions(ctx, -1, -2, -3, &ListReactionOptions{}) return err }) From eb7012e32c0bab66917e2bffc150d75550fa84d6 Mon Sep 17 00:00:00 2001 From: Colby Williams Date: Tue, 25 Mar 2025 08:35:15 -0500 Subject: [PATCH 2/3] release reaction list and delete --- github/reactions.go | 51 ++++++++++++++++++++ github/reactions_test.go | 100 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 151 insertions(+) diff --git a/github/reactions.go b/github/reactions.go index 71dd611d85d..e9710823eaf 100644 --- a/github/reactions.go +++ b/github/reactions.go @@ -568,3 +568,54 @@ func (s *ReactionsService) CreateReleaseReaction(ctx context.Context, owner, rep return m, resp, nil } + +// ListReleaseReactions lists the reactions for an release. +// +// GitHub API docs: https://docs.github.com/rest/reactions/reactions#list-reactions-for-a-release +// +//meta:operation GET /repos/{owner}/{repo}/releases/{release_id}/reactions +func (s *ReactionsService) ListReleaseReactions(ctx context.Context, owner, repo string, releaseID int64, opts *ListReactionOptions) ([]*Reaction, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/releases/%v/reactions", owner, repo, releaseID) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept headers when APIs fully launch. + req.Header.Set("Accept", mediaTypeReactionsPreview) + + var m []*Reaction + resp, err := s.client.Do(ctx, req, &m) + if err != nil { + return nil, resp, err + } + + return m, resp, nil +} + +// DeleteReleaseReaction deletes the reaction for a commit release. +// +// GitHub API docs: https://docs.github.com/rest/reactions/reactions#delete-a-release-reaction +// +//meta:operation DELETE /repos/{owner}/{repo}/releases/{release_id}/reactions/{reaction_id} +func (s *ReactionsService) DeleteReleaseReaction(ctx context.Context, owner, repo string, releaseID, reactionID int64) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/releases/%v/reactions/%v", owner, repo, releaseID, reactionID) + + return s.deleteReaction(ctx, u) +} + +// DeleteReleaseReactionByID deletes the reaction for a commit release by repository ID. +// +// GitHub API docs: https://docs.github.com/rest/reactions/reactions#delete-a-release-reaction +// +//meta:operation DELETE /repos/{owner}/{repo}/releases/{release_id}/reactions/{reaction_id} +func (s *ReactionsService) DeleteReleaseReactionByID(ctx context.Context, repoID, releaseID, reactionID int64) (*Response, error) { + u := fmt.Sprintf("repositories/%v/releases/%v/reactions/%v", repoID, releaseID, reactionID) + + return s.deleteReaction(ctx, u) +} diff --git a/github/reactions_test.go b/github/reactions_test.go index 71e8efc0a8e..7ac5e363855 100644 --- a/github/reactions_test.go +++ b/github/reactions_test.go @@ -918,3 +918,103 @@ func TestReactionService_CreateReleaseReaction(t *testing.T) { return resp, err }) } + +func TestReactionsService_ListReleaseReactions(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/releases/1/reactions", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeReactionsPreview) + testFormValues(t, r, values{"content": "+1"}) + + w.WriteHeader(http.StatusOK) + assertWrite(t, w, []byte(`[{"id":1,"user":{"login":"l","id":2},"content":"+1"}]`)) + }) + + opt := &ListReactionOptions{Content: "+1"} + ctx := context.Background() + got, _, err := client.Reactions.ListReleaseReactions(ctx, "o", "r", 1, opt) + if err != nil { + t.Errorf("ListReleaseReactions returned error: %v", err) + } + want := []*Reaction{{ID: Ptr(int64(1)), User: &User{Login: Ptr("l"), ID: Ptr(int64(2))}, Content: Ptr("+1")}} + if !cmp.Equal(got, want) { + t.Errorf("ListReleaseReactions = %+v, want %+v", got, want) + } +} + +func TestReactionsService_ListReleaseReactions_coverage(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := context.Background() + + const methodName = "ListReleaseReactions" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Reactions.ListReleaseReactions(ctx, "\n", "\n", -1, &ListReactionOptions{}) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Reactions.ListReleaseReactions(ctx, "o", "r", 1, nil) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestReactionsService_DeleteReleaseReaction(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/releases/1/reactions/2", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + testHeader(t, r, "Accept", mediaTypeReactionsPreview) + + w.WriteHeader(http.StatusNoContent) + }) + + ctx := context.Background() + if _, err := client.Reactions.DeleteReleaseReaction(ctx, "o", "r", 1, 2); err != nil { + t.Errorf("DeleteReleaseReaction returned error: %v", err) + } + + const methodName = "DeleteReleaseReaction" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Reactions.DeleteReleaseReaction(ctx, "\n", "\n", -1, -2) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Reactions.DeleteReleaseReaction(ctx, "o", "r", 1, 2) + }) +} + +func TestReactionsService_DeleteReleaseReactionByRepoID(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repositories/1/releases/2/reactions/3", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + testHeader(t, r, "Accept", mediaTypeReactionsPreview) + + w.WriteHeader(http.StatusNoContent) + }) + + ctx := context.Background() + if _, err := client.Reactions.DeleteReleaseReactionByID(ctx, 1, 2, 3); err != nil { + t.Errorf("DeleteReleaseReactionByRepoID returned error: %v", err) + } + + const methodName = "DeleteReleaseReactionByID" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Reactions.DeleteIssueReactionByID(ctx, -1, -2, -3) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Reactions.DeleteIssueReactionByID(ctx, 1, 2, 3) + }) +} From 7193318a031339961f7668c9d346fe55914aa5e6 Mon Sep 17 00:00:00 2001 From: Colby Williams Date: Wed, 26 Mar 2025 18:46:28 -0500 Subject: [PATCH 3/3] pr review feedback and other copy pasta --- github/reactions.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/github/reactions.go b/github/reactions.go index e9710823eaf..5233f78d3d4 100644 --- a/github/reactions.go +++ b/github/reactions.go @@ -569,7 +569,7 @@ func (s *ReactionsService) CreateReleaseReaction(ctx context.Context, owner, rep return m, resp, nil } -// ListReleaseReactions lists the reactions for an release. +// ListReleaseReactions lists the reactions for a release. // // GitHub API docs: https://docs.github.com/rest/reactions/reactions#list-reactions-for-a-release // @@ -598,7 +598,7 @@ func (s *ReactionsService) ListReleaseReactions(ctx context.Context, owner, repo return m, resp, nil } -// DeleteReleaseReaction deletes the reaction for a commit release. +// DeleteReleaseReaction deletes the reaction for a release. // // GitHub API docs: https://docs.github.com/rest/reactions/reactions#delete-a-release-reaction // @@ -609,7 +609,7 @@ func (s *ReactionsService) DeleteReleaseReaction(ctx context.Context, owner, rep return s.deleteReaction(ctx, u) } -// DeleteReleaseReactionByID deletes the reaction for a commit release by repository ID. +// DeleteReleaseReactionByID deletes the reaction for a release by repository ID. // // GitHub API docs: https://docs.github.com/rest/reactions/reactions#delete-a-release-reaction //