@@ -29,6 +29,7 @@ type ProjectMetadata struct {
2929 BackendProject string `json:"backend-project"`
3030 ProductionDomain string `json:"production-domain"`
3131 Facts map [string ]string `json:"facts,omitempty"`
32+ IndividualFacts map [string ]string `json:"individual_facts,omitempty"`
3233}
3334
3435// Fact represents a single fact from the Lagoon API
@@ -67,6 +68,21 @@ func parseTypes(typeStr string) []string {
6768 return types
6869}
6970
71+ // parseFactTypes parses a comma-separated string of fact types and returns a slice of fact types
72+ func parseFactTypes (factTypesStr string ) []string {
73+ if factTypesStr == "" {
74+ return []string {}
75+ }
76+
77+ // Split by comma and trim whitespace
78+ factTypes := strings .Split (factTypesStr , "," )
79+ for i , t := range factTypes {
80+ factTypes [i ] = strings .TrimSpace (t )
81+ }
82+
83+ return factTypes
84+ }
85+
7086// getProjectsByTypes fetches projects for multiple types
7187func getProjectsByTypes (ctx context.Context , client * lagoon_client.Client , types []string ) ([]string , error ) {
7288 var allArgs []string
@@ -110,11 +126,16 @@ func Metadata(ctx context.Context, c *cli.Command) error {
110126 all := c .Bool ("all" )
111127 metadataType := c .String ("type" )
112128 includeFacts := c .Bool ("include-facts" )
129+ factTypesStr := c .String ("fact-types" )
113130 args := make ([]string , 0 )
114131
115132 // Parse the type parameter to handle comma-separated values
116133 types := parseTypes (metadataType )
117134
135+ // Parse the fact-types parameter to handle comma-separated values
136+ factTypes := parseFactTypes (factTypesStr )
137+ useIndividualFacts := len (factTypes ) > 0
138+
118139 if all {
119140 // Get projects for all specified types
120141 projectArgs , err := getProjectsByTypes (ctx , client , types )
@@ -144,13 +165,27 @@ func Metadata(ctx context.Context, c *cli.Command) error {
144165 project := & schema.ProjectMetadata {}
145166 err := client .ProjectByNameMetadata (ctx , v , project )
146167 if err != nil {
168+ // Check if this is the throttling error we're looking for
169+ errStr := err .Error ()
170+ if strings .Contains (errStr , "invalid character '<' looking for beginning of value" ) {
171+ return fmt .Errorf ("API throttling detected - server returned HTML instead of JSON. Error: %v" , err )
172+ } else if strings .Contains (errStr , "decoding response" ) {
173+ return fmt .Errorf ("API response decoding error - possible throttling or server issue. Error: %v" , err )
174+ }
147175 return err
148176 }
149177
150178 // Get extended project info to access productionEnvironment field
151179 extendedProject := & schema.Project {}
152180 err = client .ProjectByNameExtended (ctx , v , extendedProject )
153181 if err != nil {
182+ // Check if this is the throttling error we're looking for
183+ errStr := err .Error ()
184+ if strings .Contains (errStr , "invalid character '<' looking for beginning of value" ) {
185+ return fmt .Errorf ("API throttling detected during extended project fetch - server returned HTML instead of JSON. Error: %v" , err )
186+ } else if strings .Contains (errStr , "decoding response" ) {
187+ return fmt .Errorf ("API response decoding error during extended project fetch - possible throttling or server issue. Error: %v" , err )
188+ }
154189 return err
155190 }
156191
@@ -164,9 +199,10 @@ func Metadata(ctx context.Context, c *cli.Command) error {
164199 ProductionDomain : project .Metadata ["production-domain" ],
165200 }
166201
167- // Fetch facts if requested
168- if includeFacts {
202+ // Fetch facts if requested or if individual fact types are specified
203+ if includeFacts || useIndividualFacts {
169204 facts := make (map [string ]string )
205+ individualFacts := make (map [string ]string )
170206
171207 // Use the specific GraphQL query to fetch facts from production environment
172208 query := `
@@ -199,7 +235,27 @@ func Metadata(ctx context.Context, c *cli.Command) error {
199235
200236 response , err := client .ProcessRaw (ctx , query , variables )
201237 if err != nil {
202- facts ["status" ] = fmt .Sprintf ("Unable to fetch facts: %v" , err )
238+ // Capture full error details for debugging
239+ errStr := err .Error ()
240+ facts ["error_type" ] = "ProcessRaw_Error"
241+ facts ["full_error" ] = errStr
242+
243+ // Check for specific error patterns
244+ if strings .Contains (errStr , "invalid character '<'" ) || strings .Contains (errStr , "decoding response" ) {
245+ facts ["status" ] = "API returned HTML error page - possible throttling or server error"
246+ // Try to extract more details from the error
247+ if strings .Contains (errStr , "invalid character '<' looking for beginning of value" ) {
248+ facts ["likely_cause" ] = "HTML_response_instead_of_JSON"
249+ }
250+ } else if strings .Contains (strings .ToLower (errStr ), "throttl" ) || strings .Contains (strings .ToLower (errStr ), "rate limit" ) {
251+ facts ["status" ] = "API throttling detected"
252+ facts ["likely_cause" ] = "Rate_limiting"
253+ } else if strings .Contains (strings .ToLower (errStr ), "timeout" ) {
254+ facts ["status" ] = "Request timeout"
255+ facts ["likely_cause" ] = "Timeout"
256+ } else {
257+ facts ["status" ] = fmt .Sprintf ("Unable to fetch facts: %v" , err )
258+ }
203259 } else {
204260 // Parse the response
205261 responseBytes , err := json .Marshal (response )
@@ -219,24 +275,48 @@ func Metadata(ctx context.Context, c *cli.Command) error {
219275 // Extract facts from production environments
220276 if len (projectResponse .ProjectByName .Environments ) > 0 {
221277 for _ , env := range projectResponse .ProjectByName .Environments {
222- for _ , fact := range env .Facts {
223- if fact .Name != "" && fact .Value != "" {
224- facts [fact .Name ] = fact .Value
278+ if env .Name == "production" || env .Name == "master" {
279+ for _ , fact := range env .Facts {
280+ if fact .Name != "" && fact .Value != "" {
281+ // Always store in facts for backward compatibility
282+ if includeFacts {
283+ facts [fact .Name ] = fact .Value
284+ }
285+
286+ // Store individual facts if specific types are requested
287+ if useIndividualFacts {
288+ for _ , requestedType := range factTypes {
289+ if fact .Name == requestedType {
290+ individualFacts [fact .Name ] = fact .Value
291+ break
292+ }
293+ }
294+ }
295+ }
225296 }
297+ } else {
298+ continue
226299 }
227300 }
228- if len (facts ) == 0 {
301+ if includeFacts && len (facts ) == 0 {
229302 facts ["status" ] = "No facts available for production environment"
230303 }
231304 } else {
232- facts ["status" ] = "No production environment found"
305+ if includeFacts {
306+ facts ["status" ] = "No production environment found"
307+ }
233308 }
234309 }
235310 }
236311 }
237312 }
238313
239- item .Facts = facts
314+ if includeFacts {
315+ item .Facts = facts
316+ }
317+ if useIndividualFacts {
318+ item .IndividualFacts = individualFacts
319+ }
240320 }
241321
242322 output .Items = append (output .Items , item )
@@ -262,6 +342,11 @@ func Metadata(ctx context.Context, c *cli.Command) error {
262342 if includeFacts {
263343 header = append (header , "Facts" )
264344 }
345+ if useIndividualFacts {
346+ for _ , factType := range factTypes {
347+ header = append (header , factType )
348+ }
349+ }
265350 writer .Write (header )
266351
267352 // Write CSV data rows
@@ -287,6 +372,17 @@ func Metadata(ctx context.Context, c *cli.Command) error {
287372 }
288373 record = append (record , factsStr )
289374 }
375+ if useIndividualFacts {
376+ for _ , factType := range factTypes {
377+ value := ""
378+ if item .IndividualFacts != nil {
379+ if val , exists := item .IndividualFacts [factType ]; exists {
380+ value = val
381+ }
382+ }
383+ record = append (record , value )
384+ }
385+ }
290386 writer .Write (record )
291387 }
292388 } else {
@@ -304,6 +400,11 @@ func Metadata(ctx context.Context, c *cli.Command) error {
304400 if includeFacts {
305401 headerCells = append (headerCells , & simpletable.Cell {Align : simpletable .AlignLeft , Text : "Facts" })
306402 }
403+ if useIndividualFacts {
404+ for _ , factType := range factTypes {
405+ headerCells = append (headerCells , & simpletable.Cell {Align : simpletable .AlignLeft , Text : factType })
406+ }
407+ }
307408 table .Header = & simpletable.Header {
308409 Cells : headerCells ,
309410 }
@@ -330,6 +431,17 @@ func Metadata(ctx context.Context, c *cli.Command) error {
330431 }
331432 r = append (r , & simpletable.Cell {Text : factsStr })
332433 }
434+ if useIndividualFacts {
435+ for _ , factType := range factTypes {
436+ value := ""
437+ if item .IndividualFacts != nil {
438+ if val , exists := item .IndividualFacts [factType ]; exists {
439+ value = val
440+ }
441+ }
442+ r = append (r , & simpletable.Cell {Text : value })
443+ }
444+ }
333445 table .Body .Cells = append (table .Body .Cells , r )
334446 }
335447 table .SetStyle (simpletable .StyleCompactLite )
0 commit comments