Repository: google/go-github
Pull request #3533 (2025-03-26 23:28:30)

Add ListReleaseReactions and DeleteReleaseReaction 
Following up on changes in https://github.com/google/go-github/pull/3532

This PR adds `List` and `Delete` functionality to reactions associated with a release

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/google/go-github/pull/3533?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 91.31%. Comparing base [(`51d32d9`)](https://app.codecov.io/gh/google/go-github/commit/51d32d940781758c57da7e20d9a9b1b3141ca6d5?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google) to head [(`0a55f8a`)](https://app.codecov.io/gh/google/go-github/commit/0a55f8a3ea2fbb6c587603bb322e4e32a88f8da2?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google).

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##           master    #3533      +/-   ##
==========================================
+ Coverage   91.30%   91.31%   +0.01%     
==========================================
  Files         184      184              
  Lines       16143    16169      +26     
==========================================
+ Hits        14739    14765      +26     
  Misses       1230     1230              
  Partials      174      174              
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/google/go-github/pull/3533?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google).

<details><summary> :rocket: New features to boost your workflow: </summary>

- :snowflake: [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
</details>
gmlewis: Thank you, @stevehipwell!
Merging.

Patch:
diff --git a/github/reactions.go b/github/reactions.go
index 71dd611d..5233f78d 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 a 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 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 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)
+}


Test patch:
diff --git a/github/reactions_test.go b/github/reactions_test.go
index 71e8efc0..7ac5e363 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)
+	})
+}