Skip to content
Merged
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
24 changes: 24 additions & 0 deletions bitbucket/reactions.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package bitbucket

import (
"context"
"fmt"

forge "github.com/git-pkgs/forge"
)

func (s *bitbucketIssueService) ListReactions(ctx context.Context, owner, repo string, number int, commentID int64) ([]forge.Reaction, error) {
return nil, fmt.Errorf("listing reactions: %w", forge.ErrNotSupported)
}

func (s *bitbucketIssueService) AddReaction(ctx context.Context, owner, repo string, number int, commentID int64, reaction string) (*forge.Reaction, error) {
return nil, fmt.Errorf("adding reaction: %w", forge.ErrNotSupported)
}

func (s *bitbucketPRService) ListReactions(ctx context.Context, owner, repo string, number int, commentID int64) ([]forge.Reaction, error) {
return nil, fmt.Errorf("listing reactions: %w", forge.ErrNotSupported)
}

func (s *bitbucketPRService) AddReaction(ctx context.Context, owner, repo string, number int, commentID int64, reaction string) (*forge.Reaction, error) {
return nil, fmt.Errorf("adding reaction: %w", forge.ErrNotSupported)
}
33 changes: 33 additions & 0 deletions bitbucket/reactions_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package bitbucket

import (
"context"
"errors"
"testing"

forge "github.com/git-pkgs/forge"
)

func TestBitbucketReactionsNotSupported(t *testing.T) {
f := New("test-token", nil)

_, err := f.Issues().ListReactions(context.Background(), "owner", "repo", 1, 42)
if !errors.Is(err, forge.ErrNotSupported) {
t.Fatalf("expected ErrNotSupported, got %v", err)
}

_, err = f.Issues().AddReaction(context.Background(), "owner", "repo", 1, 42, "+1")
if !errors.Is(err, forge.ErrNotSupported) {
t.Fatalf("expected ErrNotSupported, got %v", err)
}

_, err = f.PullRequests().ListReactions(context.Background(), "owner", "repo", 1, 42)
if !errors.Is(err, forge.ErrNotSupported) {
t.Fatalf("expected ErrNotSupported, got %v", err)
}

_, err = f.PullRequests().AddReaction(context.Background(), "owner", "repo", 1, 42, "+1")
if !errors.Is(err, forge.ErrNotSupported) {
t.Fatalf("expected ErrNotSupported, got %v", err)
}
}
16 changes: 16 additions & 0 deletions forges_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -603,6 +603,14 @@ func (m *mockIssueService) ListComments(_ context.Context, owner, repo string, n
return m.comments, nil
}

func (m *mockIssueService) ListReactions(_ context.Context, owner, repo string, number int, commentID int64) ([]Reaction, error) {
return nil, nil
}

func (m *mockIssueService) AddReaction(_ context.Context, owner, repo string, number int, commentID int64, reaction string) (*Reaction, error) {
return nil, nil
}

type mockPRService struct {
pr *PullRequest
prs []PullRequest
Expand Down Expand Up @@ -682,6 +690,14 @@ func (m *mockPRService) ListComments(_ context.Context, owner, repo string, numb
return m.comments, nil
}

func (m *mockPRService) ListReactions(_ context.Context, owner, repo string, number int, commentID int64) ([]Reaction, error) {
return nil, nil
}

func (m *mockPRService) AddReaction(_ context.Context, owner, repo string, number int, commentID int64, reaction string) (*Reaction, error) {
return nil, nil
}

type mockLabelService struct {
label *Label
labels []Label
Expand Down
74 changes: 74 additions & 0 deletions gitea/reactions.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package gitea

import (
"context"
"net/http"

"code.gitea.io/sdk/gitea"
forge "github.com/git-pkgs/forge"
)

func convertGiteaReaction(r *gitea.Reaction) forge.Reaction {
result := forge.Reaction{
Content: r.Reaction,
}
if r.User != nil {
result.User = r.User.UserName
}
return result
}

func (s *giteaIssueService) ListReactions(ctx context.Context, owner, repo string, number int, commentID int64) ([]forge.Reaction, error) {
reactions, resp, err := s.client.GetIssueCommentReactions(owner, repo, commentID)
if err != nil {
if resp != nil && resp.StatusCode == http.StatusNotFound {
return nil, forge.ErrNotFound
}
return nil, err
}
var all []forge.Reaction
for _, r := range reactions {
all = append(all, convertGiteaReaction(r))
}
return all, nil
}

func (s *giteaIssueService) AddReaction(ctx context.Context, owner, repo string, number int, commentID int64, reaction string) (*forge.Reaction, error) {
r, resp, err := s.client.PostIssueCommentReaction(owner, repo, commentID, reaction)
if err != nil {
if resp != nil && resp.StatusCode == http.StatusNotFound {
return nil, forge.ErrNotFound
}
return nil, err
}
result := convertGiteaReaction(r)
return &result, nil
}

func (s *giteaPRService) ListReactions(ctx context.Context, owner, repo string, number int, commentID int64) ([]forge.Reaction, error) {
// Gitea uses the same issue comment reactions API for PR comments
reactions, resp, err := s.client.GetIssueCommentReactions(owner, repo, commentID)
if err != nil {
if resp != nil && resp.StatusCode == http.StatusNotFound {
return nil, forge.ErrNotFound
}
return nil, err
}
var all []forge.Reaction
for _, r := range reactions {
all = append(all, convertGiteaReaction(r))
}
return all, nil
}

func (s *giteaPRService) AddReaction(ctx context.Context, owner, repo string, number int, commentID int64, reaction string) (*forge.Reaction, error) {
r, resp, err := s.client.PostIssueCommentReaction(owner, repo, commentID, reaction)
if err != nil {
if resp != nil && resp.StatusCode == http.StatusNotFound {
return nil, forge.ErrNotFound
}
return nil, err
}
result := convertGiteaReaction(r)
return &result, nil
}
113 changes: 113 additions & 0 deletions gitea/reactions_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
package gitea

import (
"context"
"encoding/json"
"net/http"
"net/http/httptest"
"testing"

forge "github.com/git-pkgs/forge"
)

func TestGiteaListIssueCommentReactions(t *testing.T) {
mux := http.NewServeMux()
mux.HandleFunc("GET /api/v1/version", giteaVersionHandler)
mux.HandleFunc("GET /api/v1/repos/testorg/testrepo/issues/comments/42/reactions", func(w http.ResponseWriter, r *http.Request) {
_ = json.NewEncoder(w).Encode([]map[string]any{
{
"user": map[string]any{"login": "alice"},
"content": "+1",
},
{
"user": map[string]any{"login": "bob"},
"content": "heart",
},
})
})

srv := httptest.NewServer(mux)
defer srv.Close()

f := New(srv.URL, "test-token", nil)
reactions, err := f.Issues().ListReactions(context.Background(), "testorg", "testrepo", 1, 42)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if len(reactions) != 2 {
t.Fatalf("expected 2 reactions, got %d", len(reactions))
}

assertEqual(t, "reactions[0].Content", "+1", reactions[0].Content)
assertEqual(t, "reactions[0].User", "alice", reactions[0].User)
assertEqual(t, "reactions[1].Content", "heart", reactions[1].Content)
assertEqual(t, "reactions[1].User", "bob", reactions[1].User)
}

func TestGiteaAddIssueCommentReaction(t *testing.T) {
mux := http.NewServeMux()
mux.HandleFunc("GET /api/v1/version", giteaVersionHandler)
mux.HandleFunc("POST /api/v1/repos/testorg/testrepo/issues/comments/42/reactions", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusCreated)
_ = json.NewEncoder(w).Encode(map[string]any{
"user": map[string]any{"login": "alice"},
"content": "rocket",
})
})

srv := httptest.NewServer(mux)
defer srv.Close()

f := New(srv.URL, "test-token", nil)
reaction, err := f.Issues().AddReaction(context.Background(), "testorg", "testrepo", 1, 42, "rocket")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}

assertEqual(t, "Content", "rocket", reaction.Content)
assertEqual(t, "User", "alice", reaction.User)
}

func TestGiteaListIssueCommentReactionsNotFound(t *testing.T) {
mux := http.NewServeMux()
mux.HandleFunc("GET /api/v1/version", giteaVersionHandler)
mux.HandleFunc("GET /api/v1/repos/testorg/testrepo/issues/comments/999/reactions", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNotFound)
})

srv := httptest.NewServer(mux)
defer srv.Close()

f := New(srv.URL, "test-token", nil)
_, err := f.Issues().ListReactions(context.Background(), "testorg", "testrepo", 1, 999)
if err != forge.ErrNotFound {
t.Fatalf("expected ErrNotFound, got %v", err)
}
}

func TestGiteaPRListCommentReactions(t *testing.T) {
mux := http.NewServeMux()
mux.HandleFunc("GET /api/v1/version", giteaVersionHandler)
mux.HandleFunc("GET /api/v1/repos/testorg/testrepo/issues/comments/50/reactions", func(w http.ResponseWriter, r *http.Request) {
_ = json.NewEncoder(w).Encode([]map[string]any{
{
"user": map[string]any{"login": "carol"},
"content": "eyes",
},
})
})

srv := httptest.NewServer(mux)
defer srv.Close()

f := New(srv.URL, "test-token", nil)
reactions, err := f.PullRequests().ListReactions(context.Background(), "testorg", "testrepo", 10, 50)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if len(reactions) != 1 {
t.Fatalf("expected 1 reaction, got %d", len(reactions))
}
assertEqual(t, "Content", "eyes", reactions[0].Content)
assertEqual(t, "User", "carol", reactions[0].User)
}
93 changes: 93 additions & 0 deletions github/reactions.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package github

import (
"context"
"net/http"

forge "github.com/git-pkgs/forge"
"github.com/google/go-github/v82/github"
)

func convertGitHubReaction(r *github.Reaction) forge.Reaction {
result := forge.Reaction{
ID: r.GetID(),
Content: r.GetContent(),
}
if u := r.GetUser(); u != nil {
result.User = u.GetLogin()
}
return result
}

func (s *gitHubIssueService) ListReactions(ctx context.Context, owner, repo string, number int, commentID int64) ([]forge.Reaction, error) {
var all []forge.Reaction
opts := &github.ListReactionOptions{
ListOptions: github.ListOptions{PerPage: 100},
}
for {
reactions, resp, err := s.client.Reactions.ListIssueCommentReactions(ctx, owner, repo, commentID, opts)
if err != nil {
if resp != nil && resp.StatusCode == http.StatusNotFound {
return nil, forge.ErrNotFound
}
return nil, err
}
for _, r := range reactions {
all = append(all, convertGitHubReaction(r))
}
if resp.NextPage == 0 {
break
}
opts.Page = resp.NextPage
}
return all, nil
}

func (s *gitHubIssueService) AddReaction(ctx context.Context, owner, repo string, number int, commentID int64, reaction string) (*forge.Reaction, error) {
r, resp, err := s.client.Reactions.CreateIssueCommentReaction(ctx, owner, repo, commentID, reaction)
if err != nil {
if resp != nil && resp.StatusCode == http.StatusNotFound {
return nil, forge.ErrNotFound
}
return nil, err
}
result := convertGitHubReaction(r)
return &result, nil
}

func (s *gitHubPRService) ListReactions(ctx context.Context, owner, repo string, number int, commentID int64) ([]forge.Reaction, error) {
// GitHub uses the same issue comment reactions API for PR comments
var all []forge.Reaction
opts := &github.ListReactionOptions{
ListOptions: github.ListOptions{PerPage: 100},
}
for {
reactions, resp, err := s.client.Reactions.ListIssueCommentReactions(ctx, owner, repo, commentID, opts)
if err != nil {
if resp != nil && resp.StatusCode == http.StatusNotFound {
return nil, forge.ErrNotFound
}
return nil, err
}
for _, r := range reactions {
all = append(all, convertGitHubReaction(r))
}
if resp.NextPage == 0 {
break
}
opts.Page = resp.NextPage
}
return all, nil
}

func (s *gitHubPRService) AddReaction(ctx context.Context, owner, repo string, number int, commentID int64, reaction string) (*forge.Reaction, error) {
r, resp, err := s.client.Reactions.CreateIssueCommentReaction(ctx, owner, repo, commentID, reaction)
if err != nil {
if resp != nil && resp.StatusCode == http.StatusNotFound {
return nil, forge.ErrNotFound
}
return nil, err
}
result := convertGitHubReaction(r)
return &result, nil
}
Loading