Skip to content
Open
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
4 changes: 4 additions & 0 deletions pkg/cmd/plugin_cmds.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,10 @@ func (ptc *pluginTemplateCmd) runPluginCmd(cmd *cobra.Command, args []string) er
return errors.New("install failed due to API key not configured, please run `stripe login` or specify the `--api-key`")
}

if isAPIKeyExpiredError(err) {
fmt.Fprintln(os.Stderr, apiKeyExpiredMessage(ptc.cfg.Profile.ProfileName))
}

log.WithFields(log.Fields{
"prefix": "pluginTemplateCmd.runPluginCmd",
}).Debug(fmt.Sprintf("Plugin command '%s' exited with error: %s", plugin.Shortname, err))
Expand Down
18 changes: 16 additions & 2 deletions pkg/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ func Execute(ctx context.Context) {
switch {
case errors.Is(err, errNotAuthenticated):
// whoami already printed output; just exit non-zero
case requests.IsAPIKeyExpiredError(err):
case isAPIKeyExpiredError(err):
fmt.Fprintln(os.Stderr, apiKeyExpiredMessage(projectNameFlag))
case isLoginRequiredError && projectNameFlag != "default":
fmt.Fprintf(os.Stderr, "You provided the project name \"%[1]s\" (either via the \"--project-name\" flag or the \"STRIPE_PROJECT_NAME\" environment variable), but no config for that project was found.\nPlease run `stripe login --project-name=%[1]s` to enable commands for this project.\n", projectNameFlag)
Expand Down Expand Up @@ -192,13 +192,27 @@ func Execute(ctx context.Context) {
}

func apiKeyExpiredMessage(profileName string) string {
if profileName == "default" {
if profileName == "" || profileName == "default" {
return "The API key for the default profile has expired. Run `stripe login` to re-authenticate.\n" +
"If you recently ran `stripe login` and still see this error, it may have authenticated a different profile — run `stripe whoami` to confirm."
}
return fmt.Sprintf("The API key for profile %q has expired. Run `stripe login --project-name=%s` to re-authenticate.", profileName, profileName)
}

func isAPIKeyExpiredError(err error) bool {
if err == nil {
return false
}

if requests.IsAPIKeyExpiredError(err) {
return true
}

errString := strings.ToLower(err.Error())
return strings.Contains(errString, "api_key_expired") ||
strings.Contains(errString, "expired api key provided")
}

var keysToReBind []string

// ReBindKeys applies the value found in viper config to the cobra flag when viper has a value (possibly from env)
Expand Down
37 changes: 37 additions & 0 deletions pkg/cmd/root_expired_key_test.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
package cmd

import (
"errors"
"fmt"
"testing"

"github.com/stretchr/testify/require"

"github.com/stripe/stripe-cli/pkg/requests"
)

func TestAPIKeyExpiredMessage_DefaultProfile(t *testing.T) {
Expand All @@ -19,3 +23,36 @@ func TestAPIKeyExpiredMessage_NamedProfile(t *testing.T) {
require.Contains(t, msg, `"work"`)
require.Contains(t, msg, "--project-name=work")
}

func TestAPIKeyExpiredMessage_EmptyProfileDefaultsToDefault(t *testing.T) {
msg := apiKeyExpiredMessage("")
require.Contains(t, msg, "default profile")
require.NotContains(t, msg, `profile ""`)
}

func TestIsAPIKeyExpiredError(t *testing.T) {
t.Run("structured request error", func(t *testing.T) {
err := requests.RequestError{
StatusCode: 401,
ErrorCode: "api_key_expired",
}
require.True(t, isAPIKeyExpiredError(err))
})

t.Run("wrapped request error", func(t *testing.T) {
err := fmt.Errorf("wrapper: %w", requests.RequestError{
StatusCode: 401,
ErrorCode: "api_key_expired",
})
require.True(t, isAPIKeyExpiredError(err))
})

t.Run("plain text plugin error", func(t *testing.T) {
err := errors.New(`rpc error: code = Unknown desc = {"error":{"code":"api_key_expired","message":"Expired API Key provided: rk_test_***123"}}`)
require.True(t, isAPIKeyExpiredError(err))
})

t.Run("other error", func(t *testing.T) {
require.False(t, isAPIKeyExpiredError(errors.New("boom")))
})
}
Loading