diff --git a/cmd/api.go b/cmd/api.go index 7e4e881..9f32eff 100644 --- a/cmd/api.go +++ b/cmd/api.go @@ -15,6 +15,7 @@ var method string var data string var format string var portal bool +var debug bool // apiCmd represents the api command var apiCmd = &cobra.Command{ @@ -27,13 +28,18 @@ var apiCmd = &cobra.Command{ var client *api.Client var err error + var opts []api.ClientOption + + if debug { + opts = append(opts, api.WithDebug()) + } if portal { - client, err = api.NewPortalClient() + client, err = api.NewPortalClient(opts...) } else if isMetalPath(path) { - client, err = api.NewMetalClient() + client, err = api.NewMetalClient(opts...) } else { - client, err = api.NewStandardClient() + client, err = api.NewStandardClient(opts...) } if err != nil { @@ -75,6 +81,7 @@ func init() { apiCmd.Flags().StringVarP(&data, "data", "d", "", "Data to send with POST/PUT requests") apiCmd.Flags().StringVarP(&format, "format", "f", "json", "Format to use for output (json or yaml)") apiCmd.Flags().BoolVar(&portal, "portal", false, "Use Equinix Portal API (cookie auth)") + apiCmd.Flags().BoolVar(&debug, "debug", false, "Enable debug logging for HTTP requests and responses") } func isMetalPath(path string) bool { diff --git a/docs/equinix_api.md b/docs/equinix_api.md index 841a6ed..2161557 100644 --- a/docs/equinix_api.md +++ b/docs/equinix_api.md @@ -14,6 +14,7 @@ equinix api [url-path] [flags] ``` -d, --data string Data to send with POST/PUT requests + --debug Enable debug logging for HTTP requests and responses -f, --format string Format to use for output (json or yaml) (default "json") -h, --help help for api -X, --method string HTTP method to use (default "GET") diff --git a/internal/api/api.go b/internal/api/api.go index 6f09138..3152909 100644 --- a/internal/api/api.go +++ b/internal/api/api.go @@ -111,7 +111,7 @@ func WithDebug() ClientOption { // NewPortalClient creates a new Client for Equinix APIs that exist under // portal.equinix.com and rely on Cookies to transmit OAuth2 tokens -func NewPortalClient() (*Client, error) { +func NewPortalClient(options ...ClientOption) (*Client, error) { client := &Client{ BaseURL: "https://portal.equinix.com/api", DefaultHeaders: standardHeaders, @@ -127,11 +127,19 @@ func NewPortalClient() (*Client, error) { client.DefaultHeaders["Accept"] = "*/*" client.DefaultHeaders["Accept-Encoding"] = "*/*" + // Apply options to potentially wrap the transport + transport := http.RoundTripper(http.DefaultTransport) + for _, opt := range options { + transport = opt(transport) + } + + client.HTTPClient.Transport = transport + return client, nil } // NewMetalClient creates a new Client for the Equinix Metal API -func NewMetalClient() (*Client, error) { +func NewMetalClient(options ...ClientOption) (*Client, error) { client := &Client{ BaseURL: "https://api.equinix.com", DefaultHeaders: standardHeaders, @@ -143,14 +151,19 @@ func NewMetalClient() (*Client, error) { return nil, errors.New("metal_auth_token not found in env or config") } client.DefaultHeaders["X-Auth-Token"] = token + + // Apply options to potentially wrap the transport + transport := http.RoundTripper(http.DefaultTransport) + for _, opt := range options { + transport = opt(transport) + } + + client.HTTPClient.Transport = transport + return client, nil } // Request makes an HTTP request to the specified API path with the given method and data. -// -// TODO: May be useful to support debug logging of requests/responses. That could -// be done here but probably better to do it with a custom Transport that is shared -// across generic and generated clients for a consistent debug experience. func (c *Client) Request(apiPath, method string, data string) ([]byte, error) { url := c.BaseURL + "/" + apiPath var body io.Reader