217 lines
5.1 KiB
Go
217 lines
5.1 KiB
Go
package list
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"github.com/shurcooL/githubv4"
|
|
|
|
"github.com/cli/cli/v2/api"
|
|
"github.com/cli/cli/v2/pkg/search"
|
|
)
|
|
|
|
type RepositoryList struct {
|
|
Owner string
|
|
Repositories []api.Repository
|
|
TotalCount int
|
|
FromSearch bool
|
|
}
|
|
|
|
type FilterOptions struct {
|
|
Visibility string // private, public, internal
|
|
Fork bool
|
|
Source bool
|
|
Language string
|
|
Topic []string
|
|
Archived bool
|
|
NonArchived bool
|
|
Fields []string
|
|
}
|
|
|
|
func listRepos(client *http.Client, hostname string, limit int, owner string, filter FilterOptions) (*RepositoryList, error) {
|
|
if filter.Language != "" || filter.Archived || filter.NonArchived || len(filter.Topic) > 0 || filter.Visibility == "internal" {
|
|
return searchRepos(client, hostname, limit, owner, filter)
|
|
}
|
|
|
|
perPage := limit
|
|
if perPage > 100 {
|
|
perPage = 100
|
|
}
|
|
|
|
variables := map[string]interface{}{
|
|
"perPage": githubv4.Int(perPage),
|
|
}
|
|
|
|
if filter.Visibility != "" {
|
|
variables["privacy"] = githubv4.RepositoryPrivacy(strings.ToUpper(filter.Visibility))
|
|
}
|
|
|
|
if filter.Fork {
|
|
variables["fork"] = githubv4.Boolean(true)
|
|
} else if filter.Source {
|
|
variables["fork"] = githubv4.Boolean(false)
|
|
}
|
|
|
|
inputs := []string{"$perPage:Int!", "$endCursor:String", "$privacy:RepositoryPrivacy", "$fork:Boolean"}
|
|
var ownerConnection string
|
|
if owner == "" {
|
|
ownerConnection = "repositoryOwner: viewer"
|
|
} else {
|
|
ownerConnection = "repositoryOwner(login: $owner)"
|
|
variables["owner"] = githubv4.String(owner)
|
|
inputs = append(inputs, "$owner:String!")
|
|
}
|
|
|
|
type result struct {
|
|
RepositoryOwner struct {
|
|
Login string
|
|
Repositories struct {
|
|
Nodes []api.Repository
|
|
TotalCount int
|
|
PageInfo struct {
|
|
HasNextPage bool
|
|
EndCursor string
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
query := fmt.Sprintf(`query RepositoryList(%s) {
|
|
%s {
|
|
login
|
|
repositories(first: $perPage, after: $endCursor, privacy: $privacy, isFork: $fork, ownerAffiliations: OWNER, orderBy: { field: PUSHED_AT, direction: DESC }) {
|
|
nodes{%s}
|
|
totalCount
|
|
pageInfo{hasNextPage,endCursor}
|
|
}
|
|
}
|
|
}`, strings.Join(inputs, ","), ownerConnection, api.RepositoryGraphQL(filter.Fields))
|
|
|
|
apiClient := api.NewClientFromHTTP(client)
|
|
listResult := RepositoryList{}
|
|
pagination:
|
|
for {
|
|
var res result
|
|
err := apiClient.GraphQL(hostname, query, variables, &res)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
owner := res.RepositoryOwner
|
|
listResult.TotalCount = owner.Repositories.TotalCount
|
|
listResult.Owner = owner.Login
|
|
|
|
for _, repo := range owner.Repositories.Nodes {
|
|
listResult.Repositories = append(listResult.Repositories, repo)
|
|
if len(listResult.Repositories) >= limit {
|
|
break pagination
|
|
}
|
|
}
|
|
|
|
if !owner.Repositories.PageInfo.HasNextPage {
|
|
break
|
|
}
|
|
variables["endCursor"] = githubv4.String(owner.Repositories.PageInfo.EndCursor)
|
|
}
|
|
|
|
return &listResult, nil
|
|
}
|
|
|
|
func searchRepos(client *http.Client, hostname string, limit int, owner string, filter FilterOptions) (*RepositoryList, error) {
|
|
type result struct {
|
|
Search struct {
|
|
RepositoryCount int
|
|
Nodes []api.Repository
|
|
PageInfo struct {
|
|
HasNextPage bool
|
|
EndCursor string
|
|
}
|
|
}
|
|
}
|
|
|
|
query := fmt.Sprintf(`query RepositoryListSearch($query:String!,$perPage:Int!,$endCursor:String) {
|
|
search(type: REPOSITORY, query: $query, first: $perPage, after: $endCursor) {
|
|
repositoryCount
|
|
nodes{...on Repository{%s}}
|
|
pageInfo{hasNextPage,endCursor}
|
|
}
|
|
}`, api.RepositoryGraphQL(filter.Fields))
|
|
|
|
perPage := limit
|
|
if perPage > 100 {
|
|
perPage = 100
|
|
}
|
|
|
|
variables := map[string]interface{}{
|
|
"query": githubv4.String(searchQuery(owner, filter)),
|
|
"perPage": githubv4.Int(perPage),
|
|
}
|
|
|
|
apiClient := api.NewClientFromHTTP(client)
|
|
listResult := RepositoryList{FromSearch: true}
|
|
pagination:
|
|
for {
|
|
var result result
|
|
err := apiClient.GraphQL(hostname, query, variables, &result)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
listResult.TotalCount = result.Search.RepositoryCount
|
|
for _, repo := range result.Search.Nodes {
|
|
if listResult.Owner == "" && repo.NameWithOwner != "" {
|
|
idx := strings.IndexRune(repo.NameWithOwner, '/')
|
|
listResult.Owner = repo.NameWithOwner[:idx]
|
|
}
|
|
listResult.Repositories = append(listResult.Repositories, repo)
|
|
if len(listResult.Repositories) >= limit {
|
|
break pagination
|
|
}
|
|
}
|
|
|
|
if !result.Search.PageInfo.HasNextPage {
|
|
break
|
|
}
|
|
variables["endCursor"] = githubv4.String(result.Search.PageInfo.EndCursor)
|
|
}
|
|
|
|
return &listResult, nil
|
|
}
|
|
|
|
func searchQuery(owner string, filter FilterOptions) string {
|
|
if owner == "" {
|
|
owner = "@me"
|
|
}
|
|
|
|
fork := "true"
|
|
if filter.Fork {
|
|
fork = "only"
|
|
} else if filter.Source {
|
|
fork = "false"
|
|
}
|
|
|
|
var archived *bool
|
|
if filter.Archived {
|
|
trueBool := true
|
|
archived = &trueBool
|
|
}
|
|
if filter.NonArchived {
|
|
falseBool := false
|
|
archived = &falseBool
|
|
}
|
|
|
|
q := search.Query{
|
|
Keywords: []string{"sort:updated-desc"},
|
|
Qualifiers: search.Qualifiers{
|
|
Archived: archived,
|
|
Fork: fork,
|
|
Is: []string{filter.Visibility},
|
|
Language: filter.Language,
|
|
Topic: filter.Topic,
|
|
User: []string{owner},
|
|
},
|
|
}
|
|
|
|
return q.StandardSearchString()
|
|
}
|