github.com/ungtb10d/cli/v2@v2.0.0-20221110210412-98537dd9d6a1/pkg/cmd/repo/list/http.go (about) 1 package list 2 3 import ( 4 "fmt" 5 "net/http" 6 "strings" 7 8 "github.com/shurcooL/githubv4" 9 10 "github.com/ungtb10d/cli/v2/api" 11 "github.com/ungtb10d/cli/v2/pkg/search" 12 ) 13 14 type RepositoryList struct { 15 Owner string 16 Repositories []api.Repository 17 TotalCount int 18 FromSearch bool 19 } 20 21 type FilterOptions struct { 22 Visibility string // private, public, internal 23 Fork bool 24 Source bool 25 Language string 26 Topic []string 27 Archived bool 28 NonArchived bool 29 Fields []string 30 } 31 32 func listRepos(client *http.Client, hostname string, limit int, owner string, filter FilterOptions) (*RepositoryList, error) { 33 if filter.Language != "" || filter.Archived || filter.NonArchived || len(filter.Topic) > 0 || filter.Visibility == "internal" { 34 return searchRepos(client, hostname, limit, owner, filter) 35 } 36 37 perPage := limit 38 if perPage > 100 { 39 perPage = 100 40 } 41 42 variables := map[string]interface{}{ 43 "perPage": githubv4.Int(perPage), 44 } 45 46 if filter.Visibility != "" { 47 variables["privacy"] = githubv4.RepositoryPrivacy(strings.ToUpper(filter.Visibility)) 48 } 49 50 if filter.Fork { 51 variables["fork"] = githubv4.Boolean(true) 52 } else if filter.Source { 53 variables["fork"] = githubv4.Boolean(false) 54 } 55 56 inputs := []string{"$perPage:Int!", "$endCursor:String", "$privacy:RepositoryPrivacy", "$fork:Boolean"} 57 var ownerConnection string 58 if owner == "" { 59 ownerConnection = "repositoryOwner: viewer" 60 } else { 61 ownerConnection = "repositoryOwner(login: $owner)" 62 variables["owner"] = githubv4.String(owner) 63 inputs = append(inputs, "$owner:String!") 64 } 65 66 type result struct { 67 RepositoryOwner struct { 68 Login string 69 Repositories struct { 70 Nodes []api.Repository 71 TotalCount int 72 PageInfo struct { 73 HasNextPage bool 74 EndCursor string 75 } 76 } 77 } 78 } 79 80 query := fmt.Sprintf(`query RepositoryList(%s) { 81 %s { 82 login 83 repositories(first: $perPage, after: $endCursor, privacy: $privacy, isFork: $fork, affiliations: OWNER, orderBy: { field: PUSHED_AT, direction: DESC }) { 84 nodes{%s} 85 totalCount 86 pageInfo{hasNextPage,endCursor} 87 } 88 } 89 }`, strings.Join(inputs, ","), ownerConnection, api.RepositoryGraphQL(filter.Fields)) 90 91 apiClient := api.NewClientFromHTTP(client) 92 listResult := RepositoryList{} 93 pagination: 94 for { 95 var res result 96 err := apiClient.GraphQL(hostname, query, variables, &res) 97 if err != nil { 98 return nil, err 99 } 100 101 owner := res.RepositoryOwner 102 listResult.TotalCount = owner.Repositories.TotalCount 103 listResult.Owner = owner.Login 104 105 for _, repo := range owner.Repositories.Nodes { 106 listResult.Repositories = append(listResult.Repositories, repo) 107 if len(listResult.Repositories) >= limit { 108 break pagination 109 } 110 } 111 112 if !owner.Repositories.PageInfo.HasNextPage { 113 break 114 } 115 variables["endCursor"] = githubv4.String(owner.Repositories.PageInfo.EndCursor) 116 } 117 118 return &listResult, nil 119 } 120 121 func searchRepos(client *http.Client, hostname string, limit int, owner string, filter FilterOptions) (*RepositoryList, error) { 122 type result struct { 123 Search struct { 124 RepositoryCount int 125 Nodes []api.Repository 126 PageInfo struct { 127 HasNextPage bool 128 EndCursor string 129 } 130 } 131 } 132 133 query := fmt.Sprintf(`query RepositoryListSearch($query:String!,$perPage:Int!,$endCursor:String) { 134 search(type: REPOSITORY, query: $query, first: $perPage, after: $endCursor) { 135 repositoryCount 136 nodes{...on Repository{%s}} 137 pageInfo{hasNextPage,endCursor} 138 } 139 }`, api.RepositoryGraphQL(filter.Fields)) 140 141 perPage := limit 142 if perPage > 100 { 143 perPage = 100 144 } 145 146 variables := map[string]interface{}{ 147 "query": githubv4.String(searchQuery(owner, filter)), 148 "perPage": githubv4.Int(perPage), 149 } 150 151 apiClient := api.NewClientFromHTTP(client) 152 listResult := RepositoryList{FromSearch: true} 153 pagination: 154 for { 155 var result result 156 err := apiClient.GraphQL(hostname, query, variables, &result) 157 if err != nil { 158 return nil, err 159 } 160 161 listResult.TotalCount = result.Search.RepositoryCount 162 for _, repo := range result.Search.Nodes { 163 if listResult.Owner == "" && repo.NameWithOwner != "" { 164 idx := strings.IndexRune(repo.NameWithOwner, '/') 165 listResult.Owner = repo.NameWithOwner[:idx] 166 } 167 listResult.Repositories = append(listResult.Repositories, repo) 168 if len(listResult.Repositories) >= limit { 169 break pagination 170 } 171 } 172 173 if !result.Search.PageInfo.HasNextPage { 174 break 175 } 176 variables["endCursor"] = githubv4.String(result.Search.PageInfo.EndCursor) 177 } 178 179 return &listResult, nil 180 } 181 182 func searchQuery(owner string, filter FilterOptions) string { 183 if owner == "" { 184 owner = "@me" 185 } 186 187 fork := "true" 188 if filter.Fork { 189 fork = "only" 190 } else if filter.Source { 191 fork = "false" 192 } 193 194 var archived *bool 195 if filter.Archived { 196 trueBool := true 197 archived = &trueBool 198 } 199 if filter.NonArchived { 200 falseBool := false 201 archived = &falseBool 202 } 203 204 q := search.Query{ 205 Keywords: []string{"sort:updated-desc"}, 206 Qualifiers: search.Qualifiers{ 207 Archived: archived, 208 Fork: fork, 209 Is: []string{filter.Visibility}, 210 Language: filter.Language, 211 Topic: filter.Topic, 212 User: owner, 213 }, 214 } 215 216 return q.String() 217 }