diff --git a/pkg/cmd/delete.go b/pkg/cmd/delete.go index a3c8deae9..70af0d819 100644 --- a/pkg/cmd/delete.go +++ b/pkg/cmd/delete.go @@ -30,6 +30,8 @@ https://stripe.com/docs/api To delete a charge: $ stripe delete /customers/cus_FROPkgsHVRRspg`, + Example: `stripe delete /v1/customers/cus_abc123 + stripe delete /v1/customers/cus_abc123 --dry-run`, RunE: gc.reqs.RunRequestsCmd, } diff --git a/pkg/cmd/get.go b/pkg/cmd/get.go index f42de4f47..c2ad06039 100644 --- a/pkg/cmd/get.go +++ b/pkg/cmd/get.go @@ -27,7 +27,8 @@ id. You can also make normal HTTP GET requests to the Stripe API by providing the API path.`, Example: `stripe get ch_1EGYgUByst5pquEtjb0EkYha stripe get cus_G6GQwbr1dWXt9O - stripe get /v1/charges --limit 50`, + stripe get /v1/charges --limit 50 + stripe get /v1/customers --dry-run`, RunE: gc.reqs.RunRequestsCmd, } diff --git a/pkg/cmd/post.go b/pkg/cmd/post.go index 52fa5d8e7..cb34986b7 100644 --- a/pkg/cmd/post.go +++ b/pkg/cmd/post.go @@ -32,7 +32,8 @@ https://stripe.com/docs/api Example: `stripe post /payment_intents \ -d amount=2000 \ -d currency=usd \ - -d "payment_method_types[]=card"`, + -d "payment_method_types[]=card" + stripe post /v1/customers -d email=test@example.com --dry-run`, RunE: gc.reqs.RunRequestsCmd, } diff --git a/pkg/cmd/resource/operation_test.go b/pkg/cmd/resource/operation_test.go index 63cd25586..c1c27d816 100644 --- a/pkg/cmd/resource/operation_test.go +++ b/pkg/cmd/resource/operation_test.go @@ -206,6 +206,8 @@ func TestRunOperationCmd_DryRun(t *testing.T) { "Authorization": "Bearer sk_test_************cdef", "Content-Type": "application/x-www-form-urlencoded", }, + AuthAvailable: true, + RequiresConfirmation: false, }}, result) } @@ -226,10 +228,12 @@ func TestRunOperationCmd_DryRun_NoAPIKey(t *testing.T) { var result requests.DryRunOutput require.NoError(t, json.Unmarshal(buf.Bytes(), &result)) require.Equal(t, requests.DryRunOutput{DryRun: requests.DryRunDetails{ - Method: "POST", - URL: "https://api.stripe.com/v1/bars/bar_123", - Params: map[string]interface{}{}, - Headers: map[string]string{"Content-Type": "application/x-www-form-urlencoded"}, + Method: "POST", + URL: "https://api.stripe.com/v1/bars/bar_123", + Params: map[string]interface{}{}, + Headers: map[string]string{"Content-Type": "application/x-www-form-urlencoded"}, + AuthAvailable: false, + RequiresConfirmation: false, }}, result) } diff --git a/pkg/requests/base.go b/pkg/requests/base.go index 93ed749b2..e39f9795d 100644 --- a/pkg/requests/base.go +++ b/pkg/requests/base.go @@ -124,10 +124,12 @@ type Base struct { // DryRunDetails contains the details of a dry-run request. type DryRunDetails struct { - Method string `json:"method"` - URL string `json:"url"` - Params map[string]interface{} `json:"params"` - Headers map[string]string `json:"headers"` + Method string `json:"method"` + URL string `json:"url"` + Params map[string]interface{} `json:"params"` + Headers map[string]string `json:"headers"` + AuthAvailable bool `json:"auth_available"` + RequiresConfirmation bool `json:"requires_confirmation"` } // DryRunOutput is the top-level output for a dry-run request. @@ -200,7 +202,7 @@ func (rb *Base) InitFlags() { rb.Cmd.Flags().BoolVarP(&rb.showHeaders, "show-headers", "s", false, "Show response headers") rb.Cmd.Flags().BoolVar(&rb.Livemode, "live", false, "Make a live request (default: test)") rb.Cmd.Flags().BoolVar(&rb.DarkStyle, "dark-style", false, "Use a darker color scheme better suited for lighter command-lines") - rb.Cmd.Flags().BoolVar(&rb.DryRun, "dry-run", false, "Preview the request without sending it") + rb.Cmd.Flags().BoolVar(&rb.DryRun, "dry-run", false, "Preview the request without sending it. Outputs JSON with request details and preflight checks (auth_available, requires_confirmation).") // Conditionally add flags for GET requests. I'm doing it here to keep `limit`, `start_after` and `ending_before` unexported if rb.Method == http.MethodGet { @@ -442,10 +444,12 @@ func (rb *Base) BuildDryRunOutput(apiKey, baseURL, path string, params *RequestP return &DryRunOutput{ DryRun: DryRunDetails{ - Method: rb.Method, - URL: fullURL, - Params: paramsMap, - Headers: headers, + Method: rb.Method, + URL: fullURL, + Params: paramsMap, + Headers: headers, + AuthAvailable: apiKey != "", + RequiresConfirmation: confirmationCommands[rb.Method], }, }, nil } diff --git a/pkg/requests/base_test.go b/pkg/requests/base_test.go index 55055fdb5..d35838732 100644 --- a/pkg/requests/base_test.go +++ b/pkg/requests/base_test.go @@ -461,6 +461,8 @@ func TestBuildDryRunOutput_V1Post(t *testing.T) { "Authorization": "Bearer sk_test_************cdef", "Content-Type": "application/x-www-form-urlencoded", }, + AuthAvailable: true, + RequiresConfirmation: false, }}, *output) } @@ -485,6 +487,8 @@ func TestBuildDryRunOutput_V1PostDataParams(t *testing.T) { Headers: map[string]string{ "Content-Type": "application/x-www-form-urlencoded", }, + AuthAvailable: false, + RequiresConfirmation: false, }}, *output) } @@ -508,7 +512,9 @@ func TestBuildDryRunOutput_V1Get(t *testing.T) { "ending_before": "cus_xyz", "expand": []interface{}{"default_source"}, }, - Headers: map[string]string{}, + Headers: map[string]string{}, + AuthAvailable: false, + RequiresConfirmation: false, }}, *output) } @@ -530,6 +536,8 @@ func TestBuildDryRunOutput_V1PostExpand(t *testing.T) { Headers: map[string]string{ "Content-Type": "application/x-www-form-urlencoded", }, + AuthAvailable: false, + RequiresConfirmation: false, }}, *output) } @@ -552,6 +560,26 @@ func TestBuildDryRunOutput_V2Post(t *testing.T) { "Content-Type": "application/json", "Stripe-Version": StripeVersionHeaderValue, }, + AuthAvailable: false, + RequiresConfirmation: false, + }}, *output) +} + +func TestBuildDryRunOutput_Delete(t *testing.T) { + rb := Base{Method: http.MethodDelete} + + output, err := rb.BuildDryRunOutput("sk_test_abcd", "https://api.stripe.com", "/v1/customers/cus_abc123", &RequestParameters{}, map[string]interface{}{}) + require.NoError(t, err) + require.Equal(t, DryRunOutput{DryRun: DryRunDetails{ + Method: "DELETE", + URL: "https://api.stripe.com/v1/customers/cus_abc123", + Params: map[string]interface{}{}, + Headers: map[string]string{ + "Authorization": "Bearer sk_test_abcd", + "Content-Type": "application/x-www-form-urlencoded", + }, + AuthAvailable: true, + RequiresConfirmation: true, }}, *output) } @@ -561,10 +589,12 @@ func TestBuildDryRunOutput_NoAPIKey(t *testing.T) { output, err := rb.BuildDryRunOutput("", "https://api.stripe.com", "/v1/customers", &RequestParameters{}, map[string]interface{}{}) require.NoError(t, err) require.Equal(t, DryRunOutput{DryRun: DryRunDetails{ - Method: "POST", - URL: "https://api.stripe.com/v1/customers", - Params: map[string]interface{}{}, - Headers: map[string]string{"Content-Type": "application/x-www-form-urlencoded"}, + Method: "POST", + URL: "https://api.stripe.com/v1/customers", + Params: map[string]interface{}{}, + Headers: map[string]string{"Content-Type": "application/x-www-form-urlencoded"}, + AuthAvailable: false, + RequiresConfirmation: false, }}, *output) } @@ -574,10 +604,12 @@ func TestBuildDryRunOutput_ExplicitStripeVersion(t *testing.T) { output, err := rb.BuildDryRunOutput("", "https://api.stripe.com", "/v1/customers", &RequestParameters{version: "2025-01-01"}, map[string]interface{}{}) require.NoError(t, err) require.Equal(t, DryRunOutput{DryRun: DryRunDetails{ - Method: "POST", - URL: "https://api.stripe.com/v1/customers", - Params: map[string]interface{}{}, - Headers: map[string]string{"Content-Type": "application/x-www-form-urlencoded", "Stripe-Version": "2025-01-01"}, + Method: "POST", + URL: "https://api.stripe.com/v1/customers", + Params: map[string]interface{}{}, + Headers: map[string]string{"Content-Type": "application/x-www-form-urlencoded", "Stripe-Version": "2025-01-01"}, + AuthAvailable: false, + RequiresConfirmation: false, }}, *output) } @@ -601,6 +633,8 @@ func TestBuildDryRunOutput_OptionalHeaders(t *testing.T) { "Stripe-Account": "acct_123", "Stripe-Context": "ctx_456", }, + AuthAvailable: false, + RequiresConfirmation: false, }}, *output) } @@ -610,10 +644,12 @@ func TestBuildDryRunOutput_PathParamSubstitutedURL(t *testing.T) { output, err := rb.BuildDryRunOutput("", "https://api.stripe.com", "/v1/customers/cus_abc123", &RequestParameters{}, map[string]interface{}{}) require.NoError(t, err) require.Equal(t, DryRunOutput{DryRun: DryRunDetails{ - Method: "GET", - URL: "https://api.stripe.com/v1/customers/cus_abc123", - Params: map[string]interface{}{}, - Headers: map[string]string{}, + Method: "GET", + URL: "https://api.stripe.com/v1/customers/cus_abc123", + Params: map[string]interface{}{}, + Headers: map[string]string{}, + AuthAvailable: false, + RequiresConfirmation: false, }}, *output) }