diff --git a/api/queries_org.go b/api/queries_org.go index 0a6689b35..7366b8bc4 100644 --- a/api/queries_org.go +++ b/api/queries_org.go @@ -1,6 +1,7 @@ package api import ( + "fmt" "github.com/cli/cli/v2/internal/ghrepo" "github.com/shurcooL/githubv4" ) @@ -47,6 +48,31 @@ type OrgTeam struct { Slug string } +// OrganizationTeam fetch the team in an organization with the given slug +func OrganizationTeam(client *Client, repo ghrepo.Interface, teamSlug string) (*OrgTeam, error) { + type responseData struct { + Organization struct { + Team OrgTeam `graphql:"team(slug: $teamSlug)"` + } `graphql:"organization(login: $owner)"` + } + + variables := map[string]interface{}{ + "owner": githubv4.String(repo.RepoOwner()), + "teamSlug": githubv4.String(teamSlug), + } + + var query responseData + err := client.Query(repo.RepoHost(), "OrganizationTeam", &query, variables) + if err != nil { + return nil, err + } + if query.Organization.Team.ID == "" { + return nil, fmt.Errorf("could not resolve to a Team with the slug of '%s'", teamSlug) + } + + return &query.Organization.Team, nil +} + // OrganizationTeams fetches all the teams in an organization func OrganizationTeams(client *Client, repo ghrepo.Interface) ([]OrgTeam, error) { type responseData struct { diff --git a/pkg/cmd/project/link/link.go b/pkg/cmd/project/link/link.go index 39fdfd97e..b2744e90e 100644 --- a/pkg/cmd/project/link/link.go +++ b/pkg/cmd/project/link/link.go @@ -80,9 +80,9 @@ func NewCmdLink(f *cmdutil.Factory, runF func(config linkConfig) error) *cobra.C } if config.opts.repo != "" && config.opts.team != "" { - return fmt.Errorf("specify only one repo or team") + return fmt.Errorf("specify only one of `--repo` or `--team`") } else if config.opts.repo == "" && config.opts.team == "" { - return fmt.Errorf("specify at least one repo or team") + return fmt.Errorf("specify either `--repo` or `--team`") } // allow testing of the command without actually running it @@ -121,50 +121,49 @@ func runLink(config linkConfig) error { c := api.NewClientFromHTTP(httpClient) if config.opts.repo != "" { - repo, err := api.GitHubRepo(c, ghrepo.New(owner.Login, config.opts.repo)) - if err != nil { - return err - } - config.opts.repoID = repo.ID - - query, variable := linkRepoArgs(config) - err = config.client.Mutate("LinkProjectV2ToRepository", query, variable) - if err != nil { - return err - } - - if config.opts.exporter != nil { - return config.opts.exporter.Write(config.io, query.LinkProjectV2ToRepository.Repository) - } - return printResults(config, query.LinkProjectV2ToRepository.Repository.URL) - + return linkRepo(c, owner, config) } else if config.opts.team != "" { - teams, err := api.OrganizationTeams(c, ghrepo.New(owner.Login, "")) - if err != nil { - return err - } - for _, team := range teams { - if team.Slug == config.opts.team { - config.opts.teamID = team.ID - break - } - } - if config.opts.teamID == "" { - return fmt.Errorf("can't find team %s", config.opts.team) - } - - query, variable := linkTeamArgs(config) - err = config.client.Mutate("LinkProjectV2ToTeam", query, variable) - if err != nil { - return err - } - - if config.opts.exporter != nil { - return config.opts.exporter.Write(config.io, query.LinkProjectV2ToTeam.Team) - } - return printResults(config, query.LinkProjectV2ToTeam.Team.URL) + return linkTeam(c, owner, config) } - return fmt.Errorf("specify at least one repo or team") + return nil +} + +func linkRepo(c *api.Client, owner *queries.Owner, config linkConfig) error { + repo, err := api.GitHubRepo(c, ghrepo.New(owner.Login, config.opts.repo)) + if err != nil { + return err + } + config.opts.repoID = repo.ID + + query, variable := linkRepoArgs(config) + err = config.client.Mutate("LinkProjectV2ToRepository", query, variable) + if err != nil { + return err + } + + if config.opts.exporter != nil { + return config.opts.exporter.Write(config.io, query.LinkProjectV2ToRepository.Repository) + } + return printResults(config, query.LinkProjectV2ToRepository.Repository.URL) +} + +func linkTeam(c *api.Client, owner *queries.Owner, config linkConfig) error { + team, err := api.OrganizationTeam(c, ghrepo.New(owner.Login, ""), config.opts.team) + if err != nil { + return err + } + config.opts.teamID = team.ID + + query, variable := linkTeamArgs(config) + err = config.client.Mutate("LinkProjectV2ToTeam", query, variable) + if err != nil { + return err + } + + if config.opts.exporter != nil { + return config.opts.exporter.Write(config.io, query.LinkProjectV2ToTeam.Team) + } + return printResults(config, query.LinkProjectV2ToTeam.Team.URL) } func linkRepoArgs(config linkConfig) (*linkProjectToRepoMutation, map[string]interface{}) { diff --git a/pkg/cmd/project/link/link_test.go b/pkg/cmd/project/link/link_test.go index c1c32cb79..11d55983e 100644 --- a/pkg/cmd/project/link/link_test.go +++ b/pkg/cmd/project/link/link_test.go @@ -31,13 +31,13 @@ func TestNewCmdLink(t *testing.T) { name: "specify-nothing", cli: "", wantsErr: true, - wantsErrMsg: "specify at least one repo or team", + wantsErrMsg: "specify either `--repo` or `--team`", }, { name: "specify-repo-and-team", cli: "--repo my-repo --team my-team", wantsErr: true, - wantsErrMsg: "specify only one repo or team", + wantsErrMsg: "specify only one of `--repo` or `--team`", }, { name: "repo", @@ -316,8 +316,8 @@ func TestRunLink_Team(t *testing.T) { defer httpReg.Verify(t) httpReg.Register( - httpmock.GraphQL(`query OrganizationTeamList\b`), - httpmock.StringResponse(`{"data":{"organization":{"teams":{"nodes": [{"id": "team-ID", "slug": "my-team"}]}}}}`), + httpmock.GraphQL(`query OrganizationTeam\b`), + httpmock.StringResponse(`{"data":{"organization":{"team":{"id": "team-ID"}}}}`), ) httpClient := newTestClient(httpReg) diff --git a/pkg/cmd/project/unlink/unlink.go b/pkg/cmd/project/unlink/unlink.go index ffed68f61..a361b3843 100644 --- a/pkg/cmd/project/unlink/unlink.go +++ b/pkg/cmd/project/unlink/unlink.go @@ -80,9 +80,9 @@ func NewCmdUnlink(f *cmdutil.Factory, runF func(config unlinkConfig) error) *cob } if config.opts.repo != "" && config.opts.team != "" { - return fmt.Errorf("specify only one repo or team") + return fmt.Errorf("specify only one of `--repo` or `--team`") } else if config.opts.repo == "" && config.opts.team == "" { - return fmt.Errorf("specify at least one repo or team") + return fmt.Errorf("specify either `--repo` or `--team`") } // allow testing of the command without actually running it @@ -121,50 +121,49 @@ func runUnlink(config unlinkConfig) error { c := api.NewClientFromHTTP(httpClient) if config.opts.repo != "" { - repo, err := api.GitHubRepo(c, ghrepo.New(owner.Login, config.opts.repo)) - if err != nil { - return err - } - config.opts.repoID = repo.ID - - query, variable := unlinkRepoArgs(config) - err = config.client.Mutate("UnlinkProjectV2FromRepository", query, variable) - if err != nil { - return err - } - - if config.opts.exporter != nil { - return config.opts.exporter.Write(config.io, query.UnlinkProjectV2FromRepository.Repository) - } - return printResults(config, query.UnlinkProjectV2FromRepository.Repository.URL) - + return unlinkRepo(c, owner, config) } else if config.opts.team != "" { - teams, err := api.OrganizationTeams(c, ghrepo.New(owner.Login, "")) - if err != nil { - return err - } - for _, team := range teams { - if team.Slug == config.opts.team { - config.opts.teamID = team.ID - break - } - } - if config.opts.teamID == "" { - return fmt.Errorf("can't find team %s", config.opts.team) - } - - query, variable := unlinkTeamArgs(config) - err = config.client.Mutate("UnlinkProjectV2FromTeam", query, variable) - if err != nil { - return err - } - - if config.opts.exporter != nil { - return config.opts.exporter.Write(config.io, query.UnlinkProjectV2FromTeam.Team) - } - return printResults(config, query.UnlinkProjectV2FromTeam.Team.URL) + return unlinkTeam(c, owner, config) } - return fmt.Errorf("specify at least one repo or team") + return nil +} + +func unlinkRepo(c *api.Client, owner *queries.Owner, config unlinkConfig) error { + repo, err := api.GitHubRepo(c, ghrepo.New(owner.Login, config.opts.repo)) + if err != nil { + return err + } + config.opts.repoID = repo.ID + + query, variable := unlinkRepoArgs(config) + err = config.client.Mutate("UnlinkProjectV2FromRepository", query, variable) + if err != nil { + return err + } + + if config.opts.exporter != nil { + return config.opts.exporter.Write(config.io, query.UnlinkProjectV2FromRepository.Repository) + } + return printResults(config, query.UnlinkProjectV2FromRepository.Repository.URL) +} + +func unlinkTeam(c *api.Client, owner *queries.Owner, config unlinkConfig) error { + team, err := api.OrganizationTeam(c, ghrepo.New(owner.Login, ""), config.opts.team) + if err != nil { + return err + } + config.opts.teamID = team.ID + + query, variable := unlinkTeamArgs(config) + err = config.client.Mutate("UnlinkProjectV2FromTeam", query, variable) + if err != nil { + return err + } + + if config.opts.exporter != nil { + return config.opts.exporter.Write(config.io, query.UnlinkProjectV2FromTeam.Team) + } + return printResults(config, query.UnlinkProjectV2FromTeam.Team.URL) } func unlinkRepoArgs(config unlinkConfig) (*unlinkProjectFromRepoMutation, map[string]interface{}) { diff --git a/pkg/cmd/project/unlink/unlink_test.go b/pkg/cmd/project/unlink/unlink_test.go index 9e341cd8a..410322402 100644 --- a/pkg/cmd/project/unlink/unlink_test.go +++ b/pkg/cmd/project/unlink/unlink_test.go @@ -31,13 +31,13 @@ func TestNewCmdUnlink(t *testing.T) { name: "specify-nothing", cli: "", wantsErr: true, - wantsErrMsg: "specify at least one repo or team", + wantsErrMsg: "specify either `--repo` or `--team`", }, { name: "specify-repo-and-team", cli: "--repo my-repo --team my-team", wantsErr: true, - wantsErrMsg: "specify only one repo or team", + wantsErrMsg: "specify only one of `--repo` or `--team`", }, { name: "repo", @@ -316,8 +316,8 @@ func TestRunUnlink_Team(t *testing.T) { defer httpReg.Verify(t) httpReg.Register( - httpmock.GraphQL(`query OrganizationTeamList\b`), - httpmock.StringResponse(`{"data":{"organization":{"teams":{"nodes": [{"id": "team-ID", "slug": "my-team"}]}}}}`), + httpmock.GraphQL(`query OrganizationTeam\b`), + httpmock.StringResponse(`{"data":{"organization":{"team":{"id": "team-ID"}}}}`), ) httpClient := newTestClient(httpReg)