From 8c7c64f3272df5f39391b5c83d117712fddcafc5 Mon Sep 17 00:00:00 2001 From: Marc O'Morain Date: Thu, 21 Jun 2018 22:39:00 +0100 Subject: [PATCH] Add orb command --- client/client.go | 3 +- cmd/orb.go | 101 +++++++++++++++++++++++++++++++++++++++++++ cmd/root.go | 1 + logger/logger.go | 6 ++- settings/settings.go | 16 ++++--- 5 files changed, 119 insertions(+), 8 deletions(-) create mode 100644 cmd/orb.go diff --git a/client/client.go b/client/client.go index 42cc31f5c..8eeafb252 100644 --- a/client/client.go +++ b/client/client.go @@ -2,6 +2,7 @@ package client import ( "context" + "fmt" "github.com/CircleCI-Public/circleci-cli/logger" "github.com/machinebox/graphql" @@ -15,7 +16,7 @@ func NewClient(endpoint string, logger *logger.Logger) *graphql.Client { client := graphql.NewClient(endpoint) client.Log = func(s string) { - logger.Debug(s) + logger.Debug(fmt.Sprintf("[machinebox/graphql] %s", s)) } return client diff --git a/cmd/orb.go b/cmd/orb.go new file mode 100644 index 000000000..f85cb9a8a --- /dev/null +++ b/cmd/orb.go @@ -0,0 +1,101 @@ +package cmd + +import ( + "context" + "fmt" + + "github.com/CircleCI-Public/circleci-cli/client" + "github.com/pkg/errors" + + "github.com/machinebox/graphql" + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +func newOrbCommand() *cobra.Command { + + orbListCommand := &cobra.Command{ + Use: "list", + Short: "List orbs", + RunE: listOrbs, + } + + orbCommand := &cobra.Command{ + Use: "orb", + Short: "Operate on orbs", + } + + orbCommand.AddCommand(orbListCommand) + + return orbCommand +} + +func listOrbs(cmd *cobra.Command, args []string) error { + + ctx := context.Background() + + // Define a structure that matches the result of the GQL + // query, so that we can use mapstructure to convert from + // nested maps to a strongly typed struct. + type orbList struct { + Orbs struct { + TotalCount int + Edges []struct { + Cursor string + Node struct { + Name string + } + } + PageInfo struct { + HasNextPage bool + } + } + } + + request := graphql.NewRequest(` +query ListOrbs ($after: String!) { + orbs(first: 20, after: $after) { + totalCount, + edges { + cursor, + node { + name + } + } + pageInfo { + hasNextPage + } + } +} + `) + + client := client.NewClient(viper.GetString("endpoint"), Logger) + + var result orbList + currentCursor := "" + + for { + request.Var("after", currentCursor) + err := client.Run(ctx, request, &result) + + if err != nil { + return errors.Wrap(err, "GraphQL query failed") + } + + Logger.Prettyify(result) + + fmt.Printf("Total Number Of Orbs: %d\n", result.Orbs.TotalCount) + + for i := range result.Orbs.Edges { + edge := result.Orbs.Edges[i] + currentCursor = edge.Cursor + Logger.Infof("Orb: %s\n", edge.Node.Name) + } + + if !result.Orbs.PageInfo.HasNextPage { + break + } + } + return nil + +} diff --git a/cmd/root.go b/cmd/root.go index eaf11bb32..630171b85 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -37,6 +37,7 @@ func addCommands() { rootCmd.AddCommand(collapseCommand) rootCmd.AddCommand(configureCommand) rootCmd.AddCommand(configCmd) + rootCmd.AddCommand(newOrbCommand()) // Cobra has a peculiar default behaviour: // https://github.com/spf13/cobra/issues/340 diff --git a/logger/logger.go b/logger/logger.go index f2e189413..8361f9abf 100644 --- a/logger/logger.go +++ b/logger/logger.go @@ -20,12 +20,14 @@ type Logger struct { // Later we pass this to client.NewClient so it can also log. // By default debug and error go to os.Stderr, and info goes to os.Stdout func NewLogger(verbose bool) *Logger { - return &Logger{ + result := &Logger{ log.New(os.Stderr, "", 0), log.New(os.Stdout, "", 0), log.New(os.Stderr, "", 0), verbose, } + result.Debug("Verbose Logging: %t", verbose) + return result } // Debug prints a formatted message to stderr only if verbose is set. @@ -75,7 +77,7 @@ func (l *Logger) FatalOnError(msg string, err error) { // Prettyify accepts a map of data and pretty prints it. // It's using json.MarshalIndent and printing with log.Logger.Infoln -func (l *Logger) Prettyify(data map[string]interface{}) { +func (l *Logger) Prettyify(data interface{}) { bytes, err := json.MarshalIndent(data, "", " ") if err != nil { l.error.Fatalln(err) diff --git a/settings/settings.go b/settings/settings.go index 9dae7e143..2fbf453d6 100644 --- a/settings/settings.go +++ b/settings/settings.go @@ -21,19 +21,25 @@ func UserHomeDir() string { // EnsureSettingsFileExists does just that. func EnsureSettingsFileExists(filepath, filename string) error { // TODO - handle invalid YAML config files. - _, err := os.Stat(filepath) - if !os.IsNotExist(err) { + fullPath := path.Join(filepath, filename) + + _, err := os.Stat(fullPath) + + if err == nil { return nil } - if err = os.MkdirAll(filepath, 0700); err != nil { + if !os.IsNotExist(err) { + // Filesystem error return err } - if _, err = os.Create(path.Join(filepath, filename)); err != nil { + // Create folder + if err = os.MkdirAll(filepath, 0700); err != nil { return err } - return nil + _, err = os.Create(fullPath) + return err }