CMSgov/dpc-app

View on GitHub
dpcclient/cmd/root.go

Summary

Maintainability
A
0 mins
Test Coverage
package cmd

import (
    "fmt"
    "os"

    "github.com/spf13/cobra"

    "github.com/mitchellh/go-homedir"
    "github.com/spf13/viper"
)

var (
    keyDir      string
    tokenDir    string
    environment string
    baseURL     string
    cfgFile     string
)

// rootCmd represents the base dpcclient
var rootCmd = &cobra.Command{
    Use: "dpcclient",
    PersistentPreRun: func(cmd *cobra.Command, args []string) {
        if keyDir == "" {
            keyDir = viper.GetString("keydir")
        }
        if keyDir != "" {
            validateDirectory(keyDir)
        }
        if tokenDir == "" {
            tokenDir = viper.GetString("tokendir")
        }
        if tokenDir != "" {
            validateDirectory(tokenDir)
        }
        // alas, cobra breaks vipers config-over-default priority, so we have to handle default value setting.
        // https://github.com/spf13/viper/issues/671
        if environment == "" {
            environment = viper.GetString("env")
        }
        if environment == "" {
            environment = "sandbox"
        }
        validateEnvironment()
        baseURL = fmt.Sprintf("https://%s.dpc.cms.gov/api/v1", environment)
    },
    Short: "A command line tool to explore CMS's DPC API",
    Long: `
dpcclient helps you explore CMS's DPC API by generating the things you need 
-- a PKI key pair, an auth token and an access token -- to access it. This tool 
is not intended to be a full-fledged client, but to help you along the way in 
writing one of your own.

You invoke dpcclient with a command, its argument[s], and any appropriate flags:

dpcclient command argument flags

In addition to these builtin help messages, the README has more details and 
examples for every command.
`,
}

func validateDirectory(name string) {
    if name != "" {
        dir, err := os.Stat(name)
        if err != nil {
            fmt.Printf("directory '%s' not found or invalid? %s\n", name, err)
            os.Exit(1)
        }
        if !dir.Mode().IsDir() {
            fmt.Printf("file '%s' is not a directory? %s\n", name, err)
            os.Exit(1)
        }
    }
}

func validateEnvironment() {
    switch environment {
    case "dev", "test", "sandbox":
        fmt.Printf("Base domain: %s.dpc.cms.gov", environment)
    default:
        fmt.Printf("Invalid environment: %s. Should be one of 'dev', 'test' or 'sandbox'", environment)
        os.Exit(1)
    }
}

// Execute runs the command line
func Execute() {
    if err := rootCmd.Execute(); err != nil {
        if err.Error() != "subcommand is required" {
            fmt.Println(err)
        }
        os.Exit(1)
    }
}

func init() {
    cobra.OnInitialize(initConfig)

    // global flags that apply to all commands
    rootCmd.PersistentFlags().StringVarP(&keyDir, "keydir", "k", "", "directory used to store keys (defaults to working directory)")
    rootCmd.PersistentFlags().StringVarP(&tokenDir, "tokendir", "t", "", "directory used to store tokens (defaults to working directory)")
    // do not set default value here, as a default value here will always override the config file. https://github.com/spf13/viper/issues/671
    rootCmd.PersistentFlags().StringVarP(&environment, "env", "e", "", "the target environment name (defaults to 'sandbox')")
    rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (defaults to $HOME/.dpcclient.yaml)")
    bindViperOrExit("keydir")
    bindViperOrExit("tokendir")
    bindViperOrExit("env")
}

func bindViperOrExit(key string) {
    if err := viper.BindPFlag(key, rootCmd.PersistentFlags().Lookup(key)); err != nil {
        fmt.Println("Internal configuration error: ", err)
        os.Exit(1)
    }
}

// initConfig reads in config file and ENV variables if set.
func initConfig() {
    if cfgFile != "" {
        // Use config file from the flag.
        viper.SetConfigFile(cfgFile)
    } else {
        // Find home directory.
        home, err := homedir.Dir()
        if err != nil {
            fmt.Println(err)
            os.Exit(1)
        }

        // Search for config in home directory with name ".dpcclient" (without extension).
        viper.AddConfigPath(home)
        viper.SetConfigName(".dpcclient")
    }

    viper.AutomaticEnv() // read in environment variables that match

    // If a config file is found, read it in.
    if err := viper.ReadInConfig(); err == nil {
        fmt.Println("Using config file:", viper.ConfigFileUsed())
    }
}