2727package configuration
2828
2929import (
30+ "github.com/fatih/color"
31+ "github.com/olekukonko/tablewriter"
3032 "github.com/spf13/cobra"
3133 "github.com/spf13/viper"
3234 "github.com/briandowns/spinner"
@@ -35,6 +37,7 @@ import (
3537 "io"
3638 "github.com/sirupsen/logrus"
3739 "os"
40+ "sort"
3841 "github.com/AliceO2Group/Control/common/logger"
3942 "fmt"
4043 "strings"
@@ -64,6 +67,15 @@ const (
6467 logicError = iota // Logic/Output error
6568)
6669
70+ var (
71+ white = color .New (color .FgHiWhite ).SprintFunc ()
72+ blue = color .New (color .FgHiBlue ).SprintFunc ()
73+ green = color .New (color .FgHiGreen ).SprintFunc ()
74+ yellow = color .New (color .FgHiYellow ).SprintFunc ()
75+ red = color .New (color .FgHiRed ).SprintFunc ()
76+ grey = color .New (color .FgWhite ).SprintFunc ()
77+ )
78+
6779func WrapCall (call ConfigurationCall ) RunFunc {
6880 return func (cmd * cobra.Command , args []string ) {
6981 endpoint := viper .GetString ("config_endpoint" )
@@ -267,7 +279,7 @@ func History(cfg *configuration.ConsulSource, cmd *cobra.Command, args []string,
267279
268280 if len (args ) < 1 || len (args ) > 2 {
269281 err = errors .New (fmt .Sprintf (" accepts between 0 and 3 arg(s), but received %d" , len (args )))
270- return err , 1
282+ return err , invalidArgs
271283 }
272284 switch len (args ) {
273285 case 1 :
@@ -279,49 +291,47 @@ func History(cfg *configuration.ConsulSource, cmd *cobra.Command, args []string,
279291 component = splitCom [0 ]
280292 entry = splitCom [1 ]
281293 } else {
282- return errors .New (fmt .Sprintf ("Component and Entry name cannot contain `/ or `@`" )), 1
294+ return errors .New (fmt .Sprintf ("Component and Entry name cannot contain `/ or `@`" )), invalidArgs
283295 }
284296 case 2 :
285297 if IsInputSingleValidWord (args [0 ]) && IsInputSingleValidWord (args [1 ]) {
286298 component = args [0 ]
287299 entry = args [1 ]
288300 } else {
289- return errors .New (fmt .Sprintf ("Component and Entry name cannot contain `/ or `@`" )), 1
301+ return errors .New (fmt .Sprintf ("Component and Entry name cannot contain `/ or `@`" )), invalidArgs
290302 }
291303 }
292304 key = componentsPath + component + "/" + entry
293- keys , err := cfg .GetKeysByPrefix (key , "" )
294- if err != nil {
295- return errors .New (fmt .Sprintf ("Could not query ConsulSource" )), 2
296- }
305+ var keys sort.StringSlice
306+ keys , err = cfg .GetKeysByPrefix (key , "" )
297307
298308 if len (keys ) == 0 {
299- return errors .New (fmt .Sprintf ("No data was found" )), 2
309+ return errors .New (fmt .Sprintf ("No data was found" )), emptyData
300310 } else {
301- for index := range keys {
302- var value = keys [index ]
303- value = strings .TrimPrefix (value , componentsPath )
304- index2 := strings .LastIndex (value , "/" )
305- timetsamo := value [index2 + 1 :]
306- i , err := strconv .ParseInt (timetsamo , 10 , 64 )
307- if err != nil {
308- panic (err )
309- }
310- tm := time .Unix (i , 0 )
311- value = value [:index2 ] + string ("@" ) + value [index2 + 1 :] + " " + tm .Format (time .RFC822 )
312- keys [index ] = value
313- }
314311 if entry != "" {
315-
312+ sort .Sort (sort .Reverse (keys ))
313+ drawTableHistoryConfigs ([]string {}, keys , 0 , o )
316314 } else {
317- //tree
315+ maxLen := GetMaxLenOfKey (keys )
316+ var currentKeys sort.StringSlice
317+ for _ , value := range keys {
318+ _ , currentEntry , _ := GetComponentEntryTimestamp (value )
319+ if currentEntry != entry {
320+ if entry != "" {
321+ fmt .Fprintln (o , "- " + entry )
322+ sort .Sort (sort .Reverse (currentKeys ))
323+ drawTableHistoryConfigs ([]string {}, currentKeys ,maxLen , o )
324+ }
325+ currentKeys = []string {value }
326+ entry = currentEntry
327+ } else {
328+ currentKeys = append (currentKeys , value )
329+ }
330+ }
331+ fmt .Fprintln (o , "- " + entry )
332+ drawTableHistoryConfigs ([]string {}, currentKeys ,maxLen , o )
318333 }
319334 }
320- output , err := formatListOutput (cmd , keys )
321- if err != nil {
322- return err , 4
323- }
324- fmt .Fprintln (o , string (output ))
325335 return nil , 0
326336}
327337
@@ -348,10 +358,36 @@ func IsInputNameValid(input string) bool {
348358 return InputRegex .MatchString (input )
349359}
350360
361+ func GetMapOfKeysAndTimestamps (keys []string )(map [string ]string , error , int ) {
362+ configMap := make (map [string ]string )
363+ for index := range keys {
364+ var value = keys [index ]
365+ value = strings .TrimPrefix (value , componentsPath )
366+ indexOfTimestamp := strings .LastIndex (value , "/" )
367+ timestamp , err := GetTimestampInFormat (value [indexOfTimestamp + 1 :], time .RFC822 )
368+ if err != nil {
369+ return nil , err , logicError
370+ }
371+ value = value [:indexOfTimestamp ] + string ("@" ) + value [indexOfTimestamp + 1 :]
372+ configMap [value ] = timestamp
373+ }
374+ return configMap , nil , 0
375+ }
376+
351377func IsInputSingleValidWord (input string ) bool {
352378 return ! strings .Contains (input , "/" ) && ! strings .Contains (input , "@" )
353379}
354380
381+ // Method to parse a timestamp in the specified format
382+ func GetTimestampInFormat (timestamp string , timeFormat string )(string , error ){
383+ timeStampAsInt , err := strconv .ParseInt (timestamp , 10 , 64 )
384+ if err != nil {
385+ return "" , errors .New (fmt .Sprintf ("Unable to identify timestamp" ))
386+ }
387+ tm := time .Unix (timeStampAsInt , 0 )
388+ return tm .Format (timeFormat ), nil
389+ }
390+
355391// Method to return the latest timestamp for a specified component & entry
356392// If no keys were passed an error and code exit 3 will be returned
357393func GetLatestTimestamp (keys []string , component string , entry string )(timestamp string , err error , code int ) {
@@ -406,4 +442,44 @@ func GetListOfComponentsAndOrWithTimestamps(keys []string, keyPrefix string, use
406442 }
407443 }
408444 return components , nil , nonZero
409- }
445+ }
446+
447+ func drawTableHistoryConfigs (headers []string , history []string , max int , o io.Writer ) {
448+ table := tablewriter .NewWriter (o )
449+ if len (headers ) > 0 {
450+ table .SetHeader (headers )
451+ }
452+ table .SetBorder (false )
453+ table .SetColMinWidth (0 , max )
454+
455+ for _ , value := range history {
456+ component , entry , timestamp := GetComponentEntryTimestamp (value )
457+ prettyTimestamp , err := GetTimestampInFormat (timestamp , time .RFC822 )
458+ if err != nil {
459+ prettyTimestamp = timestamp
460+ }
461+ configName := red (component ) + "/" + blue (entry ) + "@" + timestamp
462+ table .Append ([]string {configName , prettyTimestamp })
463+ }
464+ table .Render ()
465+ }
466+
467+ func GetComponentEntryTimestamp (key string )(component string , entry string , timestamp string ) {
468+ key = strings .TrimPrefix (key , componentsPath )
469+ key = strings .TrimPrefix (key , "/'" )
470+ key = strings .TrimSuffix (key , "/" )
471+ elements := strings .Split (key , "/" )
472+ component = elements [0 ]
473+ entry = elements [1 ]
474+ timestamp = elements [2 ]
475+ return
476+ }
477+
478+ func GetMaxLenOfKey (keys []string ) (maxLen int ){
479+ maxLen = 0
480+ for _ , value := range keys {
481+ if len (value ) - len (componentsPath ) >= maxLen {
482+ maxLen = len (value ) - len (componentsPath )
483+ }
484+ }
485+ }
0 commit comments