Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature Request 261 #262

Merged
merged 3 commits into from
Aug 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions cmd/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ var body string
var prettyPrint bool
var autoPaginate int = 0
var port int
var verbose bool

var generateCount int

Expand Down Expand Up @@ -89,6 +90,7 @@ func init() {

apiCmd.PersistentFlags().StringArrayVarP(&queryParameters, "query-params", "q", nil, "Available multiple times. Passes in query parameters to endpoints using the format of `key=value`.")
apiCmd.PersistentFlags().StringVarP(&body, "body", "b", "", "Passes a body to the request. Alteratively supports CURL-like references to files using the format of `@data,json`.")
apiCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "Whether to display HTTP request and header information above the response of the API call.")

// default here is false to enable -p commands to toggle off without explicitly defining -p=false as -p false will not work. The below commands invert the bool to pass the true default. Deprecated, so marking as hidden in favor of the unformatted flag.
apiCmd.PersistentFlags().BoolVarP(&prettyPrint, "pretty-print", "p", false, "Whether to pretty-print API requests. Default is true.")
Expand Down Expand Up @@ -127,9 +129,9 @@ func cmdRun(cmd *cobra.Command, args []string) error {
}

if cmd.Name() == "get" && cmd.PersistentFlags().Lookup("autopaginate").Changed {
return api.NewRequest(cmd.Name(), path, queryParameters, []byte(body), !prettyPrint, &autoPaginate)
return api.NewRequest(cmd.Name(), path, queryParameters, []byte(body), !prettyPrint, &autoPaginate, verbose)
} else {
return api.NewRequest(cmd.Name(), path, queryParameters, []byte(body), !prettyPrint, nil) // only set on when the user changed the flag
return api.NewRequest(cmd.Name(), path, queryParameters, []byte(body), !prettyPrint, nil, verbose) // only set on when the user changed the flag
}
}

Expand Down
1 change: 1 addition & 0 deletions docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ Allows the user to make GET calls to endpoints on Helix. Requires a logged in to
| `--query-param` | `-q` | Query parameters for the endpoint in `key=value` format. Multiple can be entered to give multiple parameters. | `get -q login=ninja` | N |
| `--unformatted` | `-u` | Whether to return unformatted responses. Default is `false`. | `get -u` | N |
| `--autopaginate` | `-P` | Whether to autopaginate the response from Twitch, and optionally the number of pages to limit. **WARNING** This flag can cause extremely large payloads and cause issues with some terminals. Default is to not autopaginate, however if provided, the default is gets all responses. | `get -P=10` | N |
| `--verbose` | `-v` | Whether to display HTTP request and header information above the response of the API call. | `get -v` | N |

**Examples**

Expand Down
55 changes: 54 additions & 1 deletion internal/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,18 @@ type clientInformation struct {
}

// NewRequest is used to request data from the Twitch API using a HTTP GET request- this function is a wrapper for the apiRequest function that handles the network call
func NewRequest(method string, path string, queryParameters []string, body []byte, prettyPrint bool, autopaginate *int) error {
func NewRequest(method string, path string, queryParameters []string, body []byte, prettyPrint bool, autopaginate *int, verbose bool) error {
var data models.APIResponse
var err error
var cursor string

var requestMethod string
var requestPath string
var requestHeaders http.Header
var responseHeaders http.Header
var responseStatusCode int
var protocol string

isExtensionsLiveEndpoint := false // https://github.com/twitchdev/twitch-cli/issues/157

data.Data = make([]interface{}, 0)
Expand Down Expand Up @@ -130,6 +137,13 @@ func NewRequest(method string, path string, queryParameters []string, body []byt

if runCounter == 1 {
data = apiResponse

requestMethod = resp.HttpMethod
requestPath = resp.RequestPath
responseStatusCode = resp.StatusCode
requestHeaders = resp.RequestHeaders
responseHeaders = resp.ResponseHeaders
protocol = resp.HttpVersion
}

if resp.StatusCode > 299 || resp.StatusCode < 200 {
Expand Down Expand Up @@ -210,6 +224,10 @@ func NewRequest(method string, path string, queryParameters []string, body []byt
// since Command Prompt/Powershell don't support coloring, will pretty print without colors
if runtime.GOOS == "windows" {
s, _ := json.MarshalIndent(obj, "", " ")

if verbose {
printVerboseHeaders(requestMethod, requestPath, requestHeaders, responseHeaders, responseStatusCode, protocol)
}
if data.Error == "" {
fmt.Println(string(s))
} else {
Expand All @@ -226,6 +244,10 @@ func NewRequest(method string, path string, queryParameters []string, body []byt
if err != nil {
return err
}

if verbose {
printVerboseHeaders(requestMethod, requestPath, requestHeaders, responseHeaders, responseStatusCode, protocol)
}
if data.Error == "" {
fmt.Println(string(s))
} else {
Expand All @@ -234,6 +256,9 @@ func NewRequest(method string, path string, queryParameters []string, body []byt
return nil
}

if verbose {
printVerboseHeaders(requestMethod, requestPath, requestHeaders, responseHeaders, responseStatusCode, protocol)
}
if data.Error == "" {
fmt.Println(string(d))
} else {
Expand Down Expand Up @@ -297,3 +322,31 @@ func GetClientInformation() (clientInformation, error) {

return clientInformation{Token: token, ClientID: clientID}, nil
}

func printVerboseHeaders(method string, path string, requestHeaders http.Header, responseHeaders http.Header, responseStatusCode int, protocol string) {
fmt.Printf("* Using %v\n", protocol)
fmt.Printf("> %v %v\n", method, path)
for key, value := range requestHeaders {
for _, v := range value {
if strings.EqualFold(key, "authorization") {
parts := strings.Split(v, " ")
if len(parts) > 1 {
fmt.Printf("> %v: %v *****\n", key, parts[0])
} else {
fmt.Printf("> %v: *****\n", key)
}
} else {
fmt.Printf("> %v: %v\n", key, v)
}
}
}

fmt.Printf("\n")
fmt.Printf("< %v %v %v\n", protocol, responseStatusCode, http.StatusText(responseStatusCode))
for key, value := range responseHeaders {
for _, v := range value {
fmt.Printf("< %v: %v\n", key, v)
}
}
fmt.Printf("\n")
}
13 changes: 13 additions & 0 deletions internal/api/api_request.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"bytes"
"fmt"
"io/ioutil"
"net/http"
"time"

"github.com/twitchdev/twitch-cli/internal/request"
Expand All @@ -19,6 +20,12 @@ type apiRequestParameters struct {
type apiRequestResponse struct {
StatusCode int
Body []byte

HttpMethod string
RequestPath string
RequestHeaders http.Header
ResponseHeaders http.Header
HttpVersion string
}

func apiRequest(method string, url string, payload []byte, p apiRequestParameters) (apiRequestResponse, error) {
Expand Down Expand Up @@ -54,5 +61,11 @@ func apiRequest(method string, url string, payload []byte, p apiRequestParameter
return apiRequestResponse{
StatusCode: resp.StatusCode,
Body: body,

HttpMethod: req.Method,
RequestPath: req.URL.RequestURI(),
RequestHeaders: req.Header,
ResponseHeaders: resp.Header,
HttpVersion: req.Proto,
}, nil
}
10 changes: 5 additions & 5 deletions internal/api/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,17 +56,17 @@ func TestNewRequest(t *testing.T) {

defaultAutoPaginate := 0
// tests for normal get requests
NewRequest("GET", "", []string{"test=1", "test=2"}, nil, true, nil)
NewRequest("GET", "", []string{"test=1", "test=2"}, nil, false, &defaultAutoPaginate)
NewRequest("GET", "", []string{"test=1", "test=2"}, nil, true, nil, false)
NewRequest("GET", "", []string{"test=1", "test=2"}, nil, false, &defaultAutoPaginate, false)

// testing cursors autopagination
NewRequest("GET", "/cursor", []string{"test=1", "test=2"}, nil, false, &defaultAutoPaginate)
NewRequest("GET", "/cursor", []string{"test=1", "test=2"}, nil, false, &defaultAutoPaginate, false)

// testing 204 no-content apis
NewRequest("POST", "/nocontent", []string{"test=1", "test=2"}, nil, false, nil)
NewRequest("POST", "/nocontent", []string{"test=1", "test=2"}, nil, false, nil, false)

// testing 500 errors
NewRequest("GET", "/error", []string{"test=1", "test=2"}, nil, false, &defaultAutoPaginate)
NewRequest("GET", "/error", []string{"test=1", "test=2"}, nil, false, &defaultAutoPaginate, false)
}

func TestValidOptions(t *testing.T) {
Expand Down