From d2228a09355a196f775ddbfd5b8cf0ca5d778a42 Mon Sep 17 00:00:00 2001 From: Virgil Date: Thu, 2 Apr 2026 02:43:19 +0000 Subject: [PATCH] feat: add repository team management Co-Authored-By: Virgil --- repos.go | 34 ++++++++++++++++++++ repos_test.go | 88 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 122 insertions(+) diff --git a/repos.go b/repos.go index a72132a..5ed668e 100644 --- a/repos.go +++ b/repos.go @@ -257,6 +257,40 @@ func (s *RepoService) IterCollaborators(ctx context.Context, owner, repo string) return ListIter[types.User](ctx, s.client, path, nil) } +// ListRepoTeams returns all teams assigned to a repository. +func (s *RepoService) ListRepoTeams(ctx context.Context, owner, repo string) ([]types.Team, error) { + path := ResolvePath("/api/v1/repos/{owner}/{repo}/teams", pathParams("owner", owner, "repo", repo)) + return ListAll[types.Team](ctx, s.client, path, nil) +} + +// IterRepoTeams returns an iterator over all teams assigned to a repository. +func (s *RepoService) IterRepoTeams(ctx context.Context, owner, repo string) iter.Seq2[types.Team, error] { + path := ResolvePath("/api/v1/repos/{owner}/{repo}/teams", pathParams("owner", owner, "repo", repo)) + return ListIter[types.Team](ctx, s.client, path, nil) +} + +// GetRepoTeam returns a team assigned to a repository by name. +func (s *RepoService) GetRepoTeam(ctx context.Context, owner, repo, team string) (*types.Team, error) { + path := ResolvePath("/api/v1/repos/{owner}/{repo}/teams/{team}", pathParams("owner", owner, "repo", repo, "team", team)) + var out types.Team + if err := s.client.Get(ctx, path, &out); err != nil { + return nil, err + } + return &out, nil +} + +// AddRepoTeam assigns a team to a repository. +func (s *RepoService) AddRepoTeam(ctx context.Context, owner, repo, team string) error { + path := ResolvePath("/api/v1/repos/{owner}/{repo}/teams/{team}", pathParams("owner", owner, "repo", repo, "team", team)) + return s.client.Put(ctx, path, nil, nil) +} + +// DeleteRepoTeam removes a team from a repository. +func (s *RepoService) DeleteRepoTeam(ctx context.Context, owner, repo, team string) error { + path := ResolvePath("/api/v1/repos/{owner}/{repo}/teams/{team}", pathParams("owner", owner, "repo", repo, "team", team)) + return s.client.Delete(ctx, path) +} + // CheckCollaborator reports whether a user is a collaborator on a repository. func (s *RepoService) CheckCollaborator(ctx context.Context, owner, repo, collaborator string) (bool, error) { path := ResolvePath("/api/v1/repos/{owner}/{repo}/collaborators/{collaborator}", pathParams("owner", owner, "repo", repo, "collaborator", collaborator)) diff --git a/repos_test.go b/repos_test.go index 234c647..e0497f4 100644 --- a/repos_test.go +++ b/repos_test.go @@ -1670,6 +1670,94 @@ func TestRepoService_GetCollaboratorPermission_Good(t *testing.T) { } } +func TestRepoService_ListRepoTeams_Good(t *testing.T) { + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodGet { + t.Errorf("expected GET, got %s", r.Method) + } + if r.URL.Path != "/api/v1/repos/core/go-forge/teams" { + t.Errorf("wrong path: %s", r.URL.Path) + http.NotFound(w, r) + return + } + json.NewEncoder(w).Encode([]types.Team{{ID: 7, Name: "platform"}}) + })) + defer srv.Close() + + f := NewForge(srv.URL, "tok") + teams, err := f.Repos.ListRepoTeams(context.Background(), "core", "go-forge") + if err != nil { + t.Fatal(err) + } + if len(teams) != 1 || teams[0].ID != 7 || teams[0].Name != "platform" { + t.Fatalf("got %#v", teams) + } +} + +func TestRepoService_GetRepoTeam_Good(t *testing.T) { + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodGet { + t.Errorf("expected GET, got %s", r.Method) + } + if r.URL.Path != "/api/v1/repos/core/go-forge/teams/platform" { + t.Errorf("wrong path: %s", r.URL.Path) + http.NotFound(w, r) + return + } + json.NewEncoder(w).Encode(types.Team{ID: 7, Name: "platform"}) + })) + defer srv.Close() + + f := NewForge(srv.URL, "tok") + team, err := f.Repos.GetRepoTeam(context.Background(), "core", "go-forge", "platform") + if err != nil { + t.Fatal(err) + } + if team.ID != 7 || team.Name != "platform" { + t.Fatalf("got %#v", team) + } +} + +func TestRepoService_AddRepoTeam_Good(t *testing.T) { + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodPut { + t.Errorf("expected PUT, got %s", r.Method) + } + if r.URL.Path != "/api/v1/repos/core/go-forge/teams/platform" { + t.Errorf("wrong path: %s", r.URL.Path) + http.NotFound(w, r) + return + } + w.WriteHeader(http.StatusNoContent) + })) + defer srv.Close() + + f := NewForge(srv.URL, "tok") + if err := f.Repos.AddRepoTeam(context.Background(), "core", "go-forge", "platform"); err != nil { + t.Fatal(err) + } +} + +func TestRepoService_DeleteRepoTeam_Good(t *testing.T) { + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodDelete { + t.Errorf("expected DELETE, got %s", r.Method) + } + if r.URL.Path != "/api/v1/repos/core/go-forge/teams/platform" { + t.Errorf("wrong path: %s", r.URL.Path) + http.NotFound(w, r) + return + } + w.WriteHeader(http.StatusNoContent) + })) + defer srv.Close() + + f := NewForge(srv.URL, "tok") + if err := f.Repos.DeleteRepoTeam(context.Background(), "core", "go-forge", "platform"); err != nil { + t.Fatal(err) + } +} + func TestRepoService_Watch_Good(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPut {