spf13/cobra で HTTP Client を作成する
はじめに
OpenAPI 3.0 の yaml ファイルをベースに CLI を自動生成するにあたり spf13/cobra を利用しようと思っています。
今回は spf13/cobra の復習をかねて環境構築と HTTP Client を作成してみます。
spf13/cobra
spf13/cobra はgoで書かれたCLI applicationsを作成するためのライブラリです。
Kubernetes や Hugo、 CockroachDB などで利用されているようです。
spf13/cobra のインストール
go get でインストールすることができます。
$ go get -u github.com/spf13/cobra/cobra
spf13/cobra で雛形の生成
cobra にはコマンドで雛形を生成する機能があります。
viper というファイル等から設定を読み込むライブラリは今回利用しない想定なので--viper=false
を指定しcli-test
という名前で雛形を生成しています。
$ cobra init --pkg-name=github.com/<YOUR PATH>/cli-test --author=<YOUR NAME> --license=mit --viper=false
以下のような構成で雛形が生成されました。
$ tree . . ├── LICENSE ├── cmd │ └── root.go └── main.go
サブコマンドの生成
以下のコマンドでサブコマンドを生成することができます。
$ cobra add users $ tree . . ├── LICENSE ├── cmd │ ├── root.go │ ├── users.go └── main.go
生成されたusers.go
に対してfmt.Println("users")するだけのRunE
を追加します。
package cmd import ( "fmt" "github.com/spf13/cobra" ) var usersCmd = &cobra.Command{ Use: "users", Short: "A brief description", Long: `A longer description`, RunE: func(cmd *cobra.Command, args []string) error { fmt.Println("users") return nil }, } func init() { rootCmd.AddCommand(usersCmd) }
サブコマンドとして users を渡して main.go を実行するとusers
が表示されることが確認できます。
$ go run main.go users users
特定のサブコマンドに紐づくサブコマンドを生成
特定のサブコマンドに紐づいたサブコマンドは--parent=usersCmd
のようにparentとして親のサブコマンドを指定します。
ファイル名を指定できるオプションがないようなのでコマンドで生成後手で変更してます。
$ cobra add list --parent=usersCmd $ mv cmd/list.go cmd/users_list.go
生成されたusers_list.go
に対してfmt.Println(""list called")するのRunE
を追加します。
package cmd import ( "fmt" "github.com/spf13/cobra" ) var listCmd = &cobra.Command{ Use: "list", Short: "A brief description", Long: `A longer description`, RunE: func(cmd *cobra.Command, args []string) error { fmt.Println("list called") return nil }, } func init() { usersCmd.AddCommand(listCmd) }
サブコマンドとしてusers list
を渡して main.go を実行するとlist called
が表示されることが確認できます。
$ go run main.go users list list called
サブコマンドの利用を強制する
先ほどのusers.go
のRunE
を以下のように削除します。
package cmd import ( "fmt" "github.com/spf13/cobra" ) var usersCmd = &cobra.Command{ Use: "users", Short: "A brief description", Long: `A longer description`, } func init() { rootCmd.AddCommand(usersCmd) }
すると実行する対象が存在していないのでusersCmd
配下に紐づいているコマンドを実行してね!とエラーを出してくれます。
$ go run main.go users A longer description Usage: cli-test users [command] Available Commands: list A brief description Flags: -h, --help help for users Use "cli-test users [command] --help" for more information about a command. subcommand is required exit status 1
HTTP Client を実装する
users list
を叩くと example.com
へGETリクエストを出す Client を作ってみましょう。
以下のようにcmd配下にapiclient.go
を作成します。
$ tree . . ├── LICENSE ├── cmd │ ├── apiclient.go │ ├── root.go │ ├── users.go └── main.go
以下のように*http.Client
を埋め込んだapiClient構造体
を作りdoRequest
メソッドとしてHTTPのリクエストを実行します。
また、各パラメータを指定するapiParams構造体を作ります。ここのパラメータを OpenAPI3.0 の YAML ファイルをparseし値を決定していく感じです。
package cmd import ( "bytes" "io/ioutil" "log" "net" "net/http" "net/url" "strings" "time" ) type apiClient struct { httpClient *http.Client Logger *log.Logger } type apiParams struct { method string url *url.URL path string query url.Values contentType string body string } func newAPIClient() *apiClient { client := &http.Client{ Transport: &http.Transport{ Proxy: http.ProxyFromEnvironment, DialContext: (&net.Dialer{ Timeout: 30 * time.Second, KeepAlive: 30 * time.Second, DualStack: true, }).DialContext, }, Timeout: 10 * time.Second, } return &apiClient{ httpClient: client, } } func (ac *apiClient) doRequest(params *apiParams) (*http.Response, string, error) { req, _ := http.NewRequest(params.method, params.url.String(), strings.NewReader(params.body)) res, _ := ac.httpClient.Do(req) defer res.Body.Close() b, _ := ioutil.ReadAll(res.Body) return res, bytes.NewBuffer(b).String(), nil }
今回は yaml ファイルを読みこむことはしませんが、以下のようにapiParamsを埋めてdoRequestに渡すと実行してくれるはずです。
var listCmd = &cobra.Command{ Use: "list", Short: "A brief description", Long: `A longer description`, RunE: func(cmd *cobra.Command, args []string) error { ac := newAPIClient() u, _ := url.Parse("http://example.com") params := &apiParams{ method: "GET", url: u, } res, str, _ := ac.doRequest(params) fmt.Println("res:", res) fmt.Println("str:", str) return nil }, }
まとめ
spf13/cobra で HTTP Client を作成する方法をまとめました。