diff --git a/github/reactions.go b/github/reactions.go index 71dd611d85d..5233f78d3d4 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) +} 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) + }) +}