@@ -2,13 +2,157 @@ package main
22
33import (
44 "context"
5+ "encoding/json"
56 "flag"
67 "fmt"
78 "strings"
89
910 "github.com/sourcegraph/src-cli/internal/api"
1011)
1112
13+ type reposListOptions struct {
14+ first int
15+ query string
16+ cloned bool
17+ notCloned bool
18+ indexed bool
19+ notIndexed bool
20+ orderBy string
21+ descending bool
22+ }
23+
24+ type repositoriesListResult struct {
25+ Data struct {
26+ Repositories struct {
27+ Nodes []Repository `json:"nodes"`
28+ } `json:"repositories"`
29+ } `json:"data"`
30+ Errors []json.RawMessage `json:"errors,omitempty"`
31+ }
32+
33+ // listRepositories returns the repositories from the response, any GraphQL
34+ // errors returned alongside data (should be treated as warnings), and
35+ // a hard error when the query fails without usable repository data.
36+ func listRepositories (ctx context.Context , client api.Client , params reposListOptions ) ([]Repository , api.GraphQlErrors , error ) {
37+ query := `query Repositories(
38+ $first: Int,
39+ $query: String,
40+ $cloned: Boolean,
41+ $notCloned: Boolean,
42+ $indexed: Boolean,
43+ $notIndexed: Boolean,
44+ $orderBy: RepositoryOrderBy,
45+ $descending: Boolean,
46+ ) {
47+ repositories(
48+ first: $first,
49+ query: $query,
50+ cloned: $cloned,
51+ notCloned: $notCloned,
52+ indexed: $indexed,
53+ notIndexed: $notIndexed,
54+ orderBy: $orderBy,
55+ descending: $descending,
56+ ) {
57+ nodes {
58+ ...RepositoryFields
59+ }
60+ }
61+ }
62+ ` + repositoryFragment
63+
64+ var result repositoriesListResult
65+ ok , err := client .NewRequest (query , map [string ]any {
66+ "first" : api .NullInt (params .first ),
67+ "query" : api .NullString (params .query ),
68+ "cloned" : params .cloned ,
69+ "notCloned" : params .notCloned ,
70+ "indexed" : params .indexed ,
71+ "notIndexed" : params .notIndexed ,
72+ "orderBy" : params .orderBy ,
73+ "descending" : params .descending ,
74+ }).DoRaw (ctx , & result )
75+ if err != nil || ! ok {
76+ return nil , nil , err
77+ }
78+ repos := result .Data .Repositories .Nodes
79+ if len (result .Errors ) == 0 {
80+ return repos , nil , nil
81+ }
82+
83+ errors := api .NewGraphQlErrors (result .Errors )
84+ if len (repos ) > 0 {
85+ return repos , errors , nil
86+ }
87+
88+ return nil , nil , errors
89+ }
90+
91+ func gqlErrorPathString (pathSegment any ) (string , bool ) {
92+ value , ok := pathSegment .(string )
93+ return value , ok
94+ }
95+
96+ func gqlErrorIndex (pathSegment any ) (int , bool ) {
97+ switch value := pathSegment .(type ) {
98+ case float64 :
99+ index := int (value )
100+ return index , float64 (index ) == value && index >= 0
101+ case int :
102+ return value , value >= 0
103+ default :
104+ return 0 , false
105+ }
106+ }
107+
108+ func gqlWarningPath (graphQLError * api.GraphQlError ) string {
109+ path , err := graphQLError .Path ()
110+ if err != nil || len (path ) == 0 {
111+ return ""
112+ }
113+
114+ var b strings.Builder
115+ for _ , pathSegment := range path {
116+ if segment , ok := gqlErrorPathString (pathSegment ); ok {
117+ if b .Len () > 0 {
118+ b .WriteByte ('.' )
119+ }
120+ b .WriteString (segment )
121+ continue
122+ }
123+
124+ if index , ok := gqlErrorIndex (pathSegment ); ok {
125+ fmt .Fprintf (& b , "[%d]" , index )
126+ }
127+ }
128+
129+ return b .String ()
130+ }
131+
132+ func gqlWarningMessage (graphQLError * api.GraphQlError ) string {
133+ message , err := graphQLError .Message ()
134+ if err != nil || message == "" {
135+ return graphQLError .Error ()
136+ }
137+ return message
138+ }
139+
140+ func formatRepositoryListWarnings (warnings api.GraphQlErrors ) string {
141+ var b strings.Builder
142+ fmt .Fprintf (& b , "warnings: %d errors during listing\n " , len (warnings ))
143+ for _ , warning := range warnings {
144+ path := gqlWarningPath (warning )
145+ message := gqlWarningMessage (warning )
146+ if path != "" {
147+ fmt .Fprintf (& b , "%s - %s\n " , path , message )
148+ } else {
149+ fmt .Fprintf (& b , "%s\n " , message )
150+ }
151+ fmt .Fprintf (& b , "%s\n " , warning .Error ())
152+ }
153+ return b .String ()
154+ }
155+
12156func init () {
13157 usage := `
14158Examples:
@@ -64,33 +208,6 @@ Examples:
64208 return err
65209 }
66210
67- query := `query Repositories(
68- $first: Int,
69- $query: String,
70- $cloned: Boolean,
71- $notCloned: Boolean,
72- $indexed: Boolean,
73- $notIndexed: Boolean,
74- $orderBy: RepositoryOrderBy,
75- $descending: Boolean,
76- ) {
77- repositories(
78- first: $first,
79- query: $query,
80- cloned: $cloned,
81- notCloned: $notCloned,
82- indexed: $indexed,
83- notIndexed: $notIndexed,
84- orderBy: $orderBy,
85- descending: $descending,
86- ) {
87- nodes {
88- ...RepositoryFields
89- }
90- }
91- }
92- ` + repositoryFragment
93-
94211 var orderBy string
95212 switch * orderByFlag {
96213 case "name" :
@@ -101,25 +218,22 @@ Examples:
101218 return fmt .Errorf ("invalid -order-by flag value: %q" , * orderByFlag )
102219 }
103220
104- var result struct {
105- Repositories struct {
106- Nodes []Repository
107- }
108- }
109- if ok , err := client .NewRequest (query , map [string ]any {
110- "first" : api .NullInt (* firstFlag ),
111- "query" : api .NullString (* queryFlag ),
112- "cloned" : * clonedFlag ,
113- "notCloned" : * notClonedFlag ,
114- "indexed" : * indexedFlag ,
115- "notIndexed" : * notIndexedFlag ,
116- "orderBy" : orderBy ,
117- "descending" : * descendingFlag ,
118- }).Do (context .Background (), & result ); err != nil || ! ok {
221+ // if we get repos and errors during a listing, we consider the errors as warnings and the data partially complete
222+ repos , warnings , err := listRepositories (context .Background (), client , reposListOptions {
223+ first : * firstFlag ,
224+ query : * queryFlag ,
225+ cloned : * clonedFlag ,
226+ notCloned : * notClonedFlag ,
227+ indexed : * indexedFlag ,
228+ notIndexed : * notIndexedFlag ,
229+ orderBy : orderBy ,
230+ descending : * descendingFlag ,
231+ })
232+ if err != nil {
119233 return err
120234 }
121235
122- for _ , repo := range result . Repositories . Nodes {
236+ for _ , repo := range repos {
123237 if * namesWithoutHostFlag {
124238 firstSlash := strings .Index (repo .Name , "/" )
125239 fmt .Println (repo .Name [firstSlash + len ("/" ):])
@@ -130,6 +244,13 @@ Examples:
130244 return err
131245 }
132246 }
247+ if len (warnings ) > 0 {
248+ if * verbose {
249+ fmt .Fprint (flagSet .Output (), formatRepositoryListWarnings (warnings ))
250+ } else {
251+ fmt .Fprintf (flagSet .Output (), "warning: %d errors during listing; rerun with -v to inspect them\n " , len (warnings ))
252+ }
253+ }
133254 return nil
134255 }
135256
0 commit comments