diff --git a/docs/stackit_curl.md b/docs/stackit_curl.md index ef387edb5..3c2444807 100644 --- a/docs/stackit_curl.md +++ b/docs/stackit_curl.md @@ -24,6 +24,9 @@ stackit curl URL [flags] Get all the DNS zones for project with ID xxx via GET request to https://dns.api.stackit.cloud/v1/projects/xxx/zones, with header "Authorization: Bearer yyy", fail if server returns error (such as 403 Forbidden) $ stackit curl https://dns.api.stackit.cloud/v1/projects/xxx/zones -X POST -H "Authorization: Bearer yyy" --fail + + Get all the DNS zones via GET with detailed information about the request and the response + $ stackit curl https://dns.api.stackit.cloud/v1/projects/xxx/zones --verbose ``` ### Options @@ -36,6 +39,7 @@ stackit curl URL [flags] --include If set, response headers are added to the output --output string Writes output to provided file instead of printing to console -X, --request string HTTP method, defaults to GET + -v, --verbose Prints the full HTTP request and response for debugging ``` ### Options inherited from parent commands diff --git a/internal/cmd/curl/curl.go b/internal/cmd/curl/curl.go index 341654dfd..a307fa18a 100644 --- a/internal/cmd/curl/curl.go +++ b/internal/cmd/curl/curl.go @@ -31,6 +31,7 @@ const ( includeResponseHeadersFlag = "include" failOnHTTPErrorFlag = "fail" outputFileFlag = "output" + verboseFlag = "verbose" ) const ( @@ -45,6 +46,7 @@ type inputModel struct { IncludeResponseHeaders bool FailOnHTTPError bool OutputFile *string + Verbose bool } func NewCmd(params *types.CmdParams) *cobra.Command { @@ -69,6 +71,10 @@ func NewCmd(params *types.CmdParams) *cobra.Command { `Get all the DNS zones for project with ID xxx via GET request to https://dns.api.stackit.cloud/v1/projects/xxx/zones, with header "Authorization: Bearer yyy", fail if server returns error (such as 403 Forbidden)`, `$ stackit curl https://dns.api.stackit.cloud/v1/projects/xxx/zones -X POST -H "Authorization: Bearer yyy" --fail`, ), + examples.NewExample( + "Get all the DNS zones via GET with detailed information about the request and the response", + "$ stackit curl https://dns.api.stackit.cloud/v1/projects/xxx/zones --verbose", + ), ), Args: args.SingleArg(urlArg, utils.ValidateURLDomain), RunE: func(cmd *cobra.Command, args []string) (err error) { @@ -87,6 +93,11 @@ func NewCmd(params *types.CmdParams) *cobra.Command { return err } + if model.Verbose { + requestDump, _ := httputil.DumpRequest(req, true) + params.Printer.Outputln(fmt.Sprintf("--- REQUEST ---\n%s", string(requestDump))) + } + client := http.Client{ Timeout: 30 * time.Second, } @@ -94,6 +105,12 @@ func NewCmd(params *types.CmdParams) *cobra.Command { if err != nil { return fmt.Errorf("do request: %w", err) } + + if model.Verbose { + responseDump, _ := httputil.DumpResponse(resp, false) + params.Printer.Outputln(fmt.Sprintf("--- RESPONSE ---\n%s", string(responseDump))) + } + defer func() { closeErr := resp.Body.Close() if closeErr != nil { @@ -136,6 +153,7 @@ func configureFlags(cmd *cobra.Command) { cmd.Flags().Bool(includeResponseHeadersFlag, false, "If set, response headers are added to the output") cmd.Flags().Bool(failOnHTTPErrorFlag, false, "If set, exits with error 22 if response code is 4XX or 5XX") cmd.Flags().String(outputFileFlag, "", "Writes output to provided file instead of printing to console") + cmd.Flags().BoolP(verboseFlag, "v", false, "Prints the full HTTP request and response for debugging") } func parseInput(p *print.Printer, cmd *cobra.Command, inputArgs []string) (*inputModel, error) { @@ -153,6 +171,7 @@ func parseInput(p *print.Printer, cmd *cobra.Command, inputArgs []string) (*inpu IncludeResponseHeaders: flags.FlagToBoolValue(p, cmd, includeResponseHeadersFlag), FailOnHTTPError: flags.FlagToBoolValue(p, cmd, failOnHTTPErrorFlag), OutputFile: flags.FlagToStringPointer(p, cmd, outputFileFlag), + Verbose: flags.FlagToBoolValue(p, cmd, verboseFlag), } p.DebugInputModel(model) diff --git a/internal/cmd/curl/curl_test.go b/internal/cmd/curl/curl_test.go index 6f0a9b74b..1d9d0ee0c 100644 --- a/internal/cmd/curl/curl_test.go +++ b/internal/cmd/curl/curl_test.go @@ -42,6 +42,7 @@ func fixtureFlagValues(mods ...func(flagValues map[string]string)) map[string]st includeResponseHeadersFlag: "true", failOnHTTPErrorFlag: "true", outputFileFlag: "./output.txt", + verboseFlag: "false", } for _, mod := range mods { mod(flagValues) @@ -58,6 +59,7 @@ func fixtureInputModel(mods ...func(model *inputModel)) *inputModel { IncludeResponseHeaders: true, FailOnHTTPError: true, OutputFile: utils.Ptr("./output.txt"), + Verbose: false, } for _, mod := range mods { mod(model) @@ -213,6 +215,17 @@ func TestParseInput(t *testing.T) { ) }), }, + { + description: "verbose flag", + argValues: fixtureArgValues(), + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + flagValues[verboseFlag] = "true" + }), + isValid: true, + expectedModel: fixtureInputModel(func(model *inputModel) { + model.Verbose = true + }), + }, } for _, tt := range tests {