From dae6c80725edf19a9000d41eb1c4d5295d2e7449 Mon Sep 17 00:00:00 2001 From: Fatih Arslan Date: Tue, 6 Apr 2021 11:11:28 +0300 Subject: [PATCH] cmd: improved error handling to catch malformed responses This PR uses the newly added `*planetscale.Error` and its various new error codes (see: https://github.com/planetscale/planetscale-go/pull/38). The following improvement was made: * We introduced a new helper function, called `cmdutil.ErrCode()` that returns the `planetscale.ErrorCode` if the error type is `*planetscale.Error`. This is handy function that let us type switch on the `planetscale.ErrorCode` type. * We introduced a new `cmdutil.MalformedError` function that returns a human readable error message if the API returns any response that is not JSON. This is usually and indication the API is down and we receive 5xx HTML content from the LB or other intermediate proxies. * I went over all Client calls made to the API and we now fully cover every single call. We make sure to handle for `ErrNotFound` errors also to handle `ErrResponseMalformed` responses. depends on: https://github.com/planetscale/cli/pull/145 closes: https://github.com/planetscale/project-big-bang/issues/148 --- internal/cmd/backup/create.go | 9 +++-- internal/cmd/backup/delete.go | 8 +++-- internal/cmd/backup/get.go | 8 +++-- internal/cmd/backup/list.go | 8 +++-- internal/cmd/branch/create.go | 9 +++-- internal/cmd/branch/delete.go | 10 ++++-- internal/cmd/branch/get.go | 8 +++-- internal/cmd/branch/list.go | 8 +++-- internal/cmd/branch/status.go | 9 +++-- internal/cmd/database/create.go | 10 +++++- internal/cmd/database/delete.go | 8 +++-- internal/cmd/database/get.go | 8 +++-- internal/cmd/database/list.go | 9 ++++- internal/cmd/deployrequest/close.go | 10 +++++- internal/cmd/deployrequest/create.go | 10 +++++- internal/cmd/deployrequest/deploy.go | 10 +++++- internal/cmd/deployrequest/diff.go | 14 ++++++-- internal/cmd/deployrequest/list.go | 10 +++++- internal/cmd/deployrequest/review.go | 10 +++++- internal/cmd/deployrequest/show.go | 10 +++++- internal/cmd/org/list.go | 9 ++++- internal/cmd/org/switch.go | 16 +++++++-- internal/cmd/root.go | 4 +++ internal/cmd/shell/shell.go | 11 +++++- internal/cmd/snapshot/create.go | 10 +++++- internal/cmd/snapshot/get.go | 9 ++++- internal/cmd/snapshot/list.go | 11 +++++- internal/cmd/snapshot/request_deploy.go | 9 ++++- internal/cmd/token/add-access.go | 10 +++++- internal/cmd/token/create.go | 9 ++++- internal/cmd/token/delete-access.go | 10 +++++- internal/cmd/token/delete.go | 9 ++++- internal/cmd/token/get-access.go | 14 ++++++-- internal/cmd/token/list.go | 10 +++++- internal/cmdutil/errors.go | 48 +++++++++++++++++++++++++ internal/cmdutil/terminal.go | 11 ------ internal/promptutil/branch.go | 9 ++++- 37 files changed, 334 insertions(+), 61 deletions(-) create mode 100644 internal/cmdutil/errors.go diff --git a/internal/cmd/backup/create.go b/internal/cmd/backup/create.go index 50a4caba..d0d4aa4e 100644 --- a/internal/cmd/backup/create.go +++ b/internal/cmd/backup/create.go @@ -7,6 +7,7 @@ import ( "github.com/planetscale/cli/internal/cmdutil" "github.com/planetscale/cli/internal/config" "github.com/planetscale/cli/internal/printer" + "github.com/planetscale/planetscale-go/planetscale" ps "github.com/planetscale/planetscale-go/planetscale" "github.com/spf13/cobra" @@ -37,10 +38,14 @@ func CreateCmd(cfg *config.Config) *cobra.Command { defer end() backup, err := client.Backups.Create(ctx, createReq) if err != nil { - if cmdutil.IsNotFoundError(err) { + switch cmdutil.ErrCode(err) { + case planetscale.ErrNotFound: return fmt.Errorf("%s does not exist in %s", cmdutil.BoldBlue(branch), cmdutil.BoldBlue(database)) + case planetscale.ErrResponseMalformed: + return cmdutil.MalformedError(err) + default: + return err } - return err } end() diff --git a/internal/cmd/backup/delete.go b/internal/cmd/backup/delete.go index 2875287b..9fd4a477 100644 --- a/internal/cmd/backup/delete.go +++ b/internal/cmd/backup/delete.go @@ -72,10 +72,14 @@ func DeleteCmd(cfg *config.Config) *cobra.Command { Backup: backup, }) if err != nil { - if cmdutil.IsNotFoundError(err) { + switch cmdutil.ErrCode(err) { + case planetscale.ErrNotFound: return fmt.Errorf("%s does not exist in branch %s of %s\n", cmdutil.BoldBlue(backup), cmdutil.BoldBlue(branch), cmdutil.BoldBlue(database)) + case planetscale.ErrResponseMalformed: + return cmdutil.MalformedError(err) + default: + return err } - return err } end() diff --git a/internal/cmd/backup/get.go b/internal/cmd/backup/get.go index a4a5d29e..488c3e22 100644 --- a/internal/cmd/backup/get.go +++ b/internal/cmd/backup/get.go @@ -59,10 +59,14 @@ func GetCmd(cfg *config.Config) *cobra.Command { end() err = printer.PrintOutput(cfg.OutputJSON, printer.NewBackupPrinter(b)) if err != nil { - if cmdutil.IsNotFoundError(err) { + switch cmdutil.ErrCode(err) { + case planetscale.ErrNotFound: return fmt.Errorf("%s does not exist in branch %s of %s\n", cmdutil.BoldBlue(backup), cmdutil.BoldBlue(branch), cmdutil.BoldBlue(database)) + case planetscale.ErrResponseMalformed: + return cmdutil.MalformedError(err) + default: + return err } - return err } return nil diff --git a/internal/cmd/backup/list.go b/internal/cmd/backup/list.go index 0ae8e801..8f3b56f3 100644 --- a/internal/cmd/backup/list.go +++ b/internal/cmd/backup/list.go @@ -53,10 +53,14 @@ func ListCmd(cfg *config.Config) *cobra.Command { Branch: branch, }) if err != nil { - if cmdutil.IsNotFoundError(err) { + switch cmdutil.ErrCode(err) { + case planetscale.ErrNotFound: return fmt.Errorf("%s does not exist in %s\n", cmdutil.BoldBlue(branch), cmdutil.BoldBlue(database)) + case planetscale.ErrResponseMalformed: + return cmdutil.MalformedError(err) + default: + return errors.Wrap(err, "error listing backups") } - return errors.Wrap(err, "error listing backups") } end() diff --git a/internal/cmd/branch/create.go b/internal/cmd/branch/create.go index 7ba201b0..b77c5052 100644 --- a/internal/cmd/branch/create.go +++ b/internal/cmd/branch/create.go @@ -9,6 +9,7 @@ import ( "github.com/planetscale/cli/internal/cmdutil" "github.com/planetscale/cli/internal/config" "github.com/planetscale/cli/internal/printer" + "github.com/planetscale/planetscale-go/planetscale" ps "github.com/planetscale/planetscale-go/planetscale" "github.com/spf13/cobra" ) @@ -63,10 +64,14 @@ func CreateCmd(cfg *config.Config) *cobra.Command { defer end() dbBranch, err := client.DatabaseBranches.Create(ctx, createReq) if err != nil { - if cmdutil.IsNotFoundError(err) { + switch cmdutil.ErrCode(err) { + case planetscale.ErrNotFound: return fmt.Errorf("%s does not exist in %s\n", cmdutil.BoldBlue(source), cmdutil.BoldBlue(cfg.Organization)) + case planetscale.ErrResponseMalformed: + return cmdutil.MalformedError(err) + default: + return err } - return err } end() diff --git a/internal/cmd/branch/delete.go b/internal/cmd/branch/delete.go index 47ffb37b..86ddd512 100644 --- a/internal/cmd/branch/delete.go +++ b/internal/cmd/branch/delete.go @@ -70,10 +70,14 @@ func DeleteCmd(cfg *config.Config) *cobra.Command { Branch: branch, }) if err != nil { - if cmdutil.IsNotFoundError(err) { - return fmt.Errorf("%s does not exist in %s", cmdutil.BoldBlue(branch), cmdutil.BoldBlue(source)) + switch cmdutil.ErrCode(err) { + case planetscale.ErrNotFound: + return fmt.Errorf("%s does not exist in %s\n", cmdutil.BoldBlue(source), cmdutil.BoldBlue(cfg.Organization)) + case planetscale.ErrResponseMalformed: + return cmdutil.MalformedError(err) + default: + return err } - return err } end() diff --git a/internal/cmd/branch/get.go b/internal/cmd/branch/get.go index 9e636b3e..037914ae 100644 --- a/internal/cmd/branch/get.go +++ b/internal/cmd/branch/get.go @@ -51,10 +51,14 @@ func GetCmd(cfg *config.Config) *cobra.Command { Branch: branch, }) if err != nil { - if cmdutil.IsNotFoundError(err) { + switch cmdutil.ErrCode(err) { + case planetscale.ErrNotFound: return fmt.Errorf("%s does not exist in %s", cmdutil.BoldBlue(branch), cmdutil.BoldBlue(source)) + case planetscale.ErrResponseMalformed: + return cmdutil.MalformedError(err) + default: + return err } - return err } end() diff --git a/internal/cmd/branch/list.go b/internal/cmd/branch/list.go index 956fcf90..02206ab8 100644 --- a/internal/cmd/branch/list.go +++ b/internal/cmd/branch/list.go @@ -51,10 +51,14 @@ func ListCmd(cfg *config.Config) *cobra.Command { Database: database, }) if err != nil { - if cmdutil.IsNotFoundError(err) { + switch cmdutil.ErrCode(err) { + case planetscale.ErrNotFound: return fmt.Errorf("%s does not exist in %s\n", cmdutil.BoldBlue(database), cmdutil.BoldBlue(cfg.Organization)) + case planetscale.ErrResponseMalformed: + return cmdutil.MalformedError(err) + default: + return errors.Wrap(err, "error listing branches") } - return errors.Wrap(err, "error listing branches") } end() diff --git a/internal/cmd/branch/status.go b/internal/cmd/branch/status.go index d04f6c76..4462c8a0 100644 --- a/internal/cmd/branch/status.go +++ b/internal/cmd/branch/status.go @@ -37,10 +37,15 @@ func StatusCmd(cfg *config.Config) *cobra.Command { Branch: branch, }) if err != nil { - if cmdutil.IsNotFoundError(err) { + switch cmdutil.ErrCode(err) { + case planetscale.ErrNotFound: return fmt.Errorf("%s does not exist in %s", cmdutil.BoldBlue(branch), cmdutil.BoldBlue(source)) + case planetscale.ErrResponseMalformed: + return cmdutil.MalformedError(err) + default: + return err } - return err + } end() diff --git a/internal/cmd/database/create.go b/internal/cmd/database/create.go index ef5cbd22..92b82b48 100644 --- a/internal/cmd/database/create.go +++ b/internal/cmd/database/create.go @@ -9,6 +9,7 @@ import ( "github.com/planetscale/cli/internal/config" "github.com/planetscale/cli/internal/printer" + "github.com/planetscale/planetscale-go/planetscale" ps "github.com/planetscale/planetscale-go/planetscale" "github.com/pkg/browser" @@ -51,7 +52,14 @@ func CreateCmd(cfg *config.Config) *cobra.Command { defer end() database, err := client.Databases.Create(ctx, createReq) if err != nil { - return err + switch cmdutil.ErrCode(err) { + case planetscale.ErrNotFound: + return fmt.Errorf("%s does not exist in\n", cmdutil.BoldBlue(cfg.Organization)) + case planetscale.ErrResponseMalformed: + return cmdutil.MalformedError(err) + default: + return err + } } end() diff --git a/internal/cmd/database/delete.go b/internal/cmd/database/delete.go index c0572d19..27fe9d79 100644 --- a/internal/cmd/database/delete.go +++ b/internal/cmd/database/delete.go @@ -68,10 +68,14 @@ func DeleteCmd(cfg *config.Config) *cobra.Command { Database: name, }) if err != nil { - if cmdutil.IsNotFoundError(err) { + switch cmdutil.ErrCode(err) { + case planetscale.ErrNotFound: return fmt.Errorf("%s does not exist in %s\n", cmdutil.BoldBlue(name), cmdutil.BoldBlue(cfg.Organization)) + case planetscale.ErrResponseMalformed: + return cmdutil.MalformedError(err) + default: + return err } - return err } end() diff --git a/internal/cmd/database/get.go b/internal/cmd/database/get.go index 1c21c7d9..db8b4ebc 100644 --- a/internal/cmd/database/get.go +++ b/internal/cmd/database/get.go @@ -49,10 +49,14 @@ func GetCmd(cfg *config.Config) *cobra.Command { Database: name, }) if err != nil { - if cmdutil.IsNotFoundError(err) { + switch cmdutil.ErrCode(err) { + case planetscale.ErrNotFound: return fmt.Errorf("%s does not exist in %s\n", cmdutil.BoldBlue(name), cmdutil.BoldBlue(cfg.Organization)) + case planetscale.ErrResponseMalformed: + return cmdutil.MalformedError(err) + default: + return err } - return err } end() diff --git a/internal/cmd/database/list.go b/internal/cmd/database/list.go index 95d79df6..2ced0736 100644 --- a/internal/cmd/database/list.go +++ b/internal/cmd/database/list.go @@ -48,7 +48,14 @@ func ListCmd(cfg *config.Config) *cobra.Command { Organization: cfg.Organization, }) if err != nil { - return errors.Wrap(err, "error listing databases") + switch cmdutil.ErrCode(err) { + case planetscale.ErrNotFound: + return fmt.Errorf("%s does not exist\n", cmdutil.BoldBlue(cfg.Organization)) + case planetscale.ErrResponseMalformed: + return cmdutil.MalformedError(err) + default: + return errors.Wrap(err, "error listing databases") + } } end() diff --git a/internal/cmd/deployrequest/close.go b/internal/cmd/deployrequest/close.go index b65e0e6f..48521e11 100644 --- a/internal/cmd/deployrequest/close.go +++ b/internal/cmd/deployrequest/close.go @@ -39,7 +39,15 @@ func CloseCmd(cfg *config.Config) *cobra.Command { Number: n, }) if err != nil { - return err + switch cmdutil.ErrCode(err) { + case planetscale.ErrNotFound: + return fmt.Errorf("%s/%s does not exist in %s\n", + cmdutil.BoldBlue(database), cmdutil.BoldBlue(number), cmdutil.BoldBlue(cfg.Organization)) + case planetscale.ErrResponseMalformed: + return cmdutil.MalformedError(err) + default: + return err + } } fmt.Printf("Deploy request %s/%s was successfully closed!\n", diff --git a/internal/cmd/deployrequest/create.go b/internal/cmd/deployrequest/create.go index 4a4ae437..5a444085 100644 --- a/internal/cmd/deployrequest/create.go +++ b/internal/cmd/deployrequest/create.go @@ -42,7 +42,15 @@ func CreateCmd(cfg *config.Config) *cobra.Command { IntoBranch: flags.deployTo, }) if err != nil { - return err + switch cmdutil.ErrCode(err) { + case planetscale.ErrNotFound: + return fmt.Errorf("%s does not exist in %s\n", + cmdutil.BoldBlue(database), cmdutil.BoldBlue(cfg.Organization)) + case planetscale.ErrResponseMalformed: + return cmdutil.MalformedError(err) + default: + return err + } } end() diff --git a/internal/cmd/deployrequest/deploy.go b/internal/cmd/deployrequest/deploy.go index b2cf7e28..662acec0 100644 --- a/internal/cmd/deployrequest/deploy.go +++ b/internal/cmd/deployrequest/deploy.go @@ -40,7 +40,15 @@ func DeployCmd(cfg *config.Config) *cobra.Command { Number: n, }) if err != nil { - return err + switch cmdutil.ErrCode(err) { + case planetscale.ErrNotFound: + return fmt.Errorf("%s/%s does not exist in %s\n", + cmdutil.BoldBlue(database), cmdutil.BoldBlue(number), cmdutil.BoldBlue(cfg.Organization)) + case planetscale.ErrResponseMalformed: + return cmdutil.MalformedError(err) + default: + return err + } } if cfg.OutputJSON { diff --git a/internal/cmd/deployrequest/diff.go b/internal/cmd/deployrequest/diff.go index 144b123e..d36b3e1e 100644 --- a/internal/cmd/deployrequest/diff.go +++ b/internal/cmd/deployrequest/diff.go @@ -53,7 +53,15 @@ func DiffCmd(cfg *config.Config) *cobra.Command { Number: n, }) if err != nil { - return err + switch cmdutil.ErrCode(err) { + case planetscale.ErrNotFound: + return fmt.Errorf("%s/%s does not exist in %s\n", + cmdutil.BoldBlue(database), cmdutil.BoldBlue(number), cmdutil.BoldBlue(cfg.Organization)) + case planetscale.ErrResponseMalformed: + return cmdutil.MalformedError(err) + default: + return err + } } for _, df := range diffs { @@ -62,9 +70,9 @@ func DiffCmd(cfg *config.Config) *cobra.Command { for scanner.Scan() { txt := scanner.Text() if strings.HasPrefix(txt, "+") { - color.New(color.FgGreen).Add(color.Bold).Println(txt) + color.New(color.FgGreen).Add(color.Bold).Println(txt) //nolint: errcheck } else if strings.HasPrefix(txt, "-") { - color.New(color.FgRed).Add(color.Bold).Println(txt) + color.New(color.FgRed).Add(color.Bold).Println(txt) //nolint: errcheck } else { fmt.Println(txt) } diff --git a/internal/cmd/deployrequest/list.go b/internal/cmd/deployrequest/list.go index b669057e..2f582a58 100644 --- a/internal/cmd/deployrequest/list.go +++ b/internal/cmd/deployrequest/list.go @@ -52,7 +52,15 @@ func ListCmd(cfg *config.Config) *cobra.Command { Database: database, }) if err != nil { - return errors.Wrap(err, "error listing deploy requests") + switch cmdutil.ErrCode(err) { + case planetscale.ErrNotFound: + return fmt.Errorf("%s does not exist in %s\n", + cmdutil.BoldBlue(database), cmdutil.BoldBlue(cfg.Organization)) + case planetscale.ErrResponseMalformed: + return cmdutil.MalformedError(err) + default: + return errors.Wrap(err, "error listing deploy requests") + } } end() diff --git a/internal/cmd/deployrequest/review.go b/internal/cmd/deployrequest/review.go index 79e99fcb..be41f8a6 100644 --- a/internal/cmd/deployrequest/review.go +++ b/internal/cmd/deployrequest/review.go @@ -57,7 +57,15 @@ func ReviewCmd(cfg *config.Config) *cobra.Command { CommentText: flags.comment, }) if err != nil { - return err + switch cmdutil.ErrCode(err) { + case planetscale.ErrNotFound: + return fmt.Errorf("%s/%s does not exist in %s\n", + cmdutil.BoldBlue(database), cmdutil.BoldBlue(number), cmdutil.BoldBlue(cfg.Organization)) + case planetscale.ErrResponseMalformed: + return cmdutil.MalformedError(err) + default: + return err + } } switch action { diff --git a/internal/cmd/deployrequest/show.go b/internal/cmd/deployrequest/show.go index 1eacdb73..d3a24658 100644 --- a/internal/cmd/deployrequest/show.go +++ b/internal/cmd/deployrequest/show.go @@ -50,7 +50,15 @@ func ShowCmd(cfg *config.Config) *cobra.Command { Number: n, }) if err != nil { - return err + switch cmdutil.ErrCode(err) { + case planetscale.ErrNotFound: + return fmt.Errorf("%s/%s does not exist in %s\n", + cmdutil.BoldBlue(database), cmdutil.BoldBlue(number), cmdutil.BoldBlue(cfg.Organization)) + case planetscale.ErrResponseMalformed: + return cmdutil.MalformedError(err) + default: + return err + } } err = printer.PrintOutput(cfg.OutputJSON, printer.NewDeployRequestPrinter(dr)) diff --git a/internal/cmd/org/list.go b/internal/cmd/org/list.go index ed40668c..4966b035 100644 --- a/internal/cmd/org/list.go +++ b/internal/cmd/org/list.go @@ -4,8 +4,10 @@ import ( "context" "time" + "github.com/planetscale/cli/internal/cmdutil" "github.com/planetscale/cli/internal/config" "github.com/planetscale/cli/internal/printer" + "github.com/planetscale/planetscale-go/planetscale" ps "github.com/planetscale/planetscale-go/planetscale" "github.com/spf13/cobra" @@ -33,7 +35,12 @@ func ListCmd(cfg *config.Config) *cobra.Command { orgs, err := client.Organizations.List(ctx) if err != nil { - return err + switch cmdutil.ErrCode(err) { + case planetscale.ErrResponseMalformed: + return cmdutil.MalformedError(err) + default: + return err + } } err = printer.PrintOutput(cfg.OutputJSON, &printer.ObjectPrinter{ diff --git a/internal/cmd/org/switch.go b/internal/cmd/org/switch.go index 331af427..4ee03940 100644 --- a/internal/cmd/org/switch.go +++ b/internal/cmd/org/switch.go @@ -42,7 +42,14 @@ func SwitchCmd(cfg *config.Config) *cobra.Command { Organization: orgName, }) if err != nil { - return err + switch cmdutil.ErrCode(err) { + case planetscale.ErrNotFound: + return fmt.Errorf("%s does not exist\n", cmdutil.BoldBlue(orgName)) + case planetscale.ErrResponseMalformed: + return cmdutil.MalformedError(err) + default: + return err + } } end() organization = org.Name @@ -52,7 +59,12 @@ func SwitchCmd(cfg *config.Config) *cobra.Command { defer end() orgs, err := client.Organizations.List(ctx) if err != nil { - return err + switch cmdutil.ErrCode(err) { + case planetscale.ErrResponseMalformed: + return cmdutil.MalformedError(err) + default: + return err + } } end() diff --git a/internal/cmd/root.go b/internal/cmd/root.go index d4c0d72b..535996de 100644 --- a/internal/cmd/root.go +++ b/internal/cmd/root.go @@ -40,6 +40,10 @@ import ( "github.com/spf13/viper" ) +const ( + MalformedWarning = "Unexpected API response received, the PlanetScale API might be down. Please contact support with the following output" +) + var cfgFile string // rootCmd represents the base command when called without any subcommands diff --git a/internal/cmd/shell/shell.go b/internal/cmd/shell/shell.go index 58f4c165..023abf4d 100644 --- a/internal/cmd/shell/shell.go +++ b/internal/cmd/shell/shell.go @@ -19,6 +19,7 @@ import ( "github.com/planetscale/sql-proxy/proxy" "github.com/planetscale/sql-proxy/sigutil" + "github.com/planetscale/planetscale-go/planetscale" ps "github.com/planetscale/planetscale-go/planetscale" "github.com/spf13/cobra" "go.uber.org/zap" @@ -114,7 +115,15 @@ second argument: Branch: branch, }) if err != nil { - return err + switch cmdutil.ErrCode(err) { + case planetscale.ErrNotFound: + return fmt.Errorf("%s does not exist in %s", + cmdutil.BoldBlue(branch), cmdutil.BoldBlue(database)) + case planetscale.ErrResponseMalformed: + return cmdutil.MalformedError(err) + default: + return err + } } if status.Credentials.User == "" { diff --git a/internal/cmd/snapshot/create.go b/internal/cmd/snapshot/create.go index efd2ed2c..7a1e00c5 100644 --- a/internal/cmd/snapshot/create.go +++ b/internal/cmd/snapshot/create.go @@ -35,7 +35,15 @@ func CreateCmd(cfg *config.Config) *cobra.Command { Branch: branch, }) if err != nil { - return err + switch cmdutil.ErrCode(err) { + case planetscale.ErrNotFound: + return fmt.Errorf("%s does not exist in %s", + cmdutil.BoldBlue(branch), cmdutil.BoldBlue(database)) + case planetscale.ErrResponseMalformed: + return cmdutil.MalformedError(err) + default: + return err + } } end() diff --git a/internal/cmd/snapshot/get.go b/internal/cmd/snapshot/get.go index ebbaa2f8..334cc457 100644 --- a/internal/cmd/snapshot/get.go +++ b/internal/cmd/snapshot/get.go @@ -34,7 +34,14 @@ func GetCmd(cfg *config.Config) *cobra.Command { ID: id, }) if err != nil { - return err + switch cmdutil.ErrCode(err) { + case planetscale.ErrNotFound: + return fmt.Errorf("snapshot id %q does not exist", cmdutil.BoldBlue(id)) + case planetscale.ErrResponseMalformed: + return cmdutil.MalformedError(err) + default: + return err + } } end() diff --git a/internal/cmd/snapshot/list.go b/internal/cmd/snapshot/list.go index f67ac0ca..8eb9da02 100644 --- a/internal/cmd/snapshot/list.go +++ b/internal/cmd/snapshot/list.go @@ -4,6 +4,7 @@ import ( "context" "fmt" + "github.com/pkg/errors" "github.com/planetscale/cli/internal/cmdutil" "github.com/planetscale/cli/internal/config" "github.com/planetscale/cli/internal/printer" @@ -36,7 +37,15 @@ func ListCmd(cfg *config.Config) *cobra.Command { Branch: branch, }) if err != nil { - return err + switch cmdutil.ErrCode(err) { + case planetscale.ErrNotFound: + return fmt.Errorf("%s does not exist in %s\n", + cmdutil.BoldBlue(branch), cmdutil.BoldBlue(database)) + case planetscale.ErrResponseMalformed: + return cmdutil.MalformedError(err) + default: + return errors.Wrap(err, "error listing schema snapshots") + } } end() diff --git a/internal/cmd/snapshot/request_deploy.go b/internal/cmd/snapshot/request_deploy.go index a38ecbbb..460b151a 100644 --- a/internal/cmd/snapshot/request_deploy.go +++ b/internal/cmd/snapshot/request_deploy.go @@ -32,7 +32,14 @@ func RequestDeployCmd(cfg *config.Config) *cobra.Command { defer end() deployRequest, err := client.SchemaSnapshots.RequestDeploy(ctx, deployReq) if err != nil { - return err + switch cmdutil.ErrCode(err) { + case planetscale.ErrNotFound: + return fmt.Errorf("snapshot id %q does not exist", cmdutil.BoldBlue(id)) + case planetscale.ErrResponseMalformed: + return cmdutil.MalformedError(err) + default: + return err + } } end() diff --git a/internal/cmd/token/add-access.go b/internal/cmd/token/add-access.go index 00da4f74..f9242b9b 100644 --- a/internal/cmd/token/add-access.go +++ b/internal/cmd/token/add-access.go @@ -48,7 +48,15 @@ For a complete list of the access permissions that can be granted to a token, se access, err := client.ServiceTokens.AddAccess(ctx, req) if err != nil { - return err + switch cmdutil.ErrCode(err) { + case planetscale.ErrNotFound: + return fmt.Errorf("%s does not exist in %s\n", + cmdutil.BoldBlue(cfg.Database), cmdutil.BoldBlue(cfg.Organization)) + case planetscale.ErrResponseMalformed: + return cmdutil.MalformedError(err) + default: + return err + } } end() diff --git a/internal/cmd/token/create.go b/internal/cmd/token/create.go index 10facd77..521dfad9 100644 --- a/internal/cmd/token/create.go +++ b/internal/cmd/token/create.go @@ -31,7 +31,14 @@ func CreateCmd(cfg *config.Config) *cobra.Command { token, err := client.ServiceTokens.Create(ctx, req) if err != nil { - return err + switch cmdutil.ErrCode(err) { + case planetscale.ErrNotFound: + return fmt.Errorf("organization %s does not exist\n", cmdutil.BoldBlue(cfg.Organization)) + case planetscale.ErrResponseMalformed: + return cmdutil.MalformedError(err) + default: + return err + } } end() diff --git a/internal/cmd/token/delete-access.go b/internal/cmd/token/delete-access.go index 2e3d4278..1fa33ae2 100644 --- a/internal/cmd/token/delete-access.go +++ b/internal/cmd/token/delete-access.go @@ -39,7 +39,15 @@ func DeleteAccessCmd(cfg *config.Config) *cobra.Command { defer end() if err := client.ServiceTokens.DeleteAccess(ctx, req); err != nil { - return err + switch cmdutil.ErrCode(err) { + case planetscale.ErrNotFound: + return fmt.Errorf("%s does not exist in %s\n", + cmdutil.BoldBlue(cfg.Database), cmdutil.BoldBlue(cfg.Organization)) + case planetscale.ErrResponseMalformed: + return cmdutil.MalformedError(err) + default: + return err + } } end() diff --git a/internal/cmd/token/delete.go b/internal/cmd/token/delete.go index 91b6ba7f..362af527 100644 --- a/internal/cmd/token/delete.go +++ b/internal/cmd/token/delete.go @@ -36,7 +36,14 @@ func DeleteCmd(cfg *config.Config) *cobra.Command { defer end() if err := client.ServiceTokens.Delete(ctx, req); err != nil { - return err + switch cmdutil.ErrCode(err) { + case planetscale.ErrNotFound: + return fmt.Errorf("token does not exist in %s\n", cmdutil.BoldBlue(cfg.Organization)) + case planetscale.ErrResponseMalformed: + return cmdutil.MalformedError(err) + default: + return err + } } end() diff --git a/internal/cmd/token/get-access.go b/internal/cmd/token/get-access.go index 5626d884..9f9652ab 100644 --- a/internal/cmd/token/get-access.go +++ b/internal/cmd/token/get-access.go @@ -26,8 +26,10 @@ func GetCmd(cfg *config.Config) *cobra.Command { return cmd.Usage() } + name := args[0] + req := &planetscale.GetServiceTokenAccessRequest{ - ID: args[0], + ID: name, Organization: cfg.Organization, } @@ -36,7 +38,15 @@ func GetCmd(cfg *config.Config) *cobra.Command { accesses, err := client.ServiceTokens.GetAccess(ctx, req) if err != nil { - return err + switch cmdutil.ErrCode(err) { + case planetscale.ErrNotFound: + return fmt.Errorf("%s does not exist in %s\n", + cmdutil.BoldBlue(name), cmdutil.BoldBlue(cfg.Organization)) + case planetscale.ErrResponseMalformed: + return cmdutil.MalformedError(err) + default: + return err + } } end() diff --git a/internal/cmd/token/list.go b/internal/cmd/token/list.go index 1aacf998..6665eaaa 100644 --- a/internal/cmd/token/list.go +++ b/internal/cmd/token/list.go @@ -4,6 +4,7 @@ import ( "context" "fmt" + "github.com/pkg/errors" "github.com/planetscale/cli/internal/cmdutil" "github.com/planetscale/cli/internal/config" "github.com/planetscale/cli/internal/printer" @@ -31,7 +32,14 @@ func ListCmd(cfg *config.Config) *cobra.Command { tokens, err := client.ServiceTokens.List(ctx, req) if err != nil { - return err + switch cmdutil.ErrCode(err) { + case planetscale.ErrNotFound: + return fmt.Errorf("organization %s does not exist\n", cmdutil.BoldBlue(cfg.Organization)) + case planetscale.ErrResponseMalformed: + return cmdutil.MalformedError(err) + default: + return errors.Wrap(err, "error listing service tokens") + } } end() diff --git a/internal/cmdutil/errors.go b/internal/cmdutil/errors.go new file mode 100644 index 00000000..0cfd6048 --- /dev/null +++ b/internal/cmdutil/errors.go @@ -0,0 +1,48 @@ +package cmdutil + +import ( + "fmt" + + "github.com/planetscale/planetscale-go/planetscale" +) + +// ErrCode returns the code from a *planetscale.Error, if available. If the +// error is not of type *planetscale.Error or is nil, it returns an empty, +// undefined error code. +func ErrCode(err error) planetscale.ErrorCode { + if err == nil { + return "" + } + + perr, ok := err.(*planetscale.Error) + if !ok { + return "" + } + + return perr.Code + +} + +// MalformedError checks whether the given err is an *planetscale.Error and +// returns a descriptive, human readable error if the error code is of type +// planetscale.ErrResponseMalformed. If the error doesn't match these +// requirements, err is returned unmodified. +func MalformedError(err error) error { + if err == nil { + return err + } + + perr, ok := err.(*planetscale.Error) + if !ok { + return err + } + + if perr.Code != planetscale.ErrResponseMalformed { + return err + } + + const malformedWarning = "Unexpected API response received, the PlanetScale API might be down." + + " Please contact support with the following output" + + return fmt.Errorf("%s:\n\n%s", malformedWarning, perr.Meta["body"]) +} diff --git a/internal/cmdutil/terminal.go b/internal/cmdutil/terminal.go index cbe72fe2..9b814667 100644 --- a/internal/cmdutil/terminal.go +++ b/internal/cmdutil/terminal.go @@ -8,7 +8,6 @@ import ( "github.com/briandowns/spinner" "github.com/fatih/color" "github.com/mattn/go-isatty" - "github.com/planetscale/planetscale-go/planetscale" ) var IsTTY = isatty.IsTerminal(os.Stdout.Fd()) || isatty.IsCygwinTerminal(os.Stdout.Fd()) @@ -49,13 +48,3 @@ func Bold(msg string) string { // the 'color' package already handles IsTTY gracefully return color.New(color.Bold).Sprint(msg) } - -func IsNotFoundError(err error) bool { - if pErr, ok := err.(*planetscale.Error); ok { - if pErr.Code == planetscale.ErrNotFound { - return true - } - } - - return false -} diff --git a/internal/promptutil/branch.go b/internal/promptutil/branch.go index 60298f9f..9a0addce 100644 --- a/internal/promptutil/branch.go +++ b/internal/promptutil/branch.go @@ -5,6 +5,8 @@ import ( "fmt" "github.com/AlecAivazis/survey/v2" + "github.com/pkg/errors" + "github.com/planetscale/planetscale-go/planetscale" ps "github.com/planetscale/planetscale-go/planetscale" "github.com/planetscale/cli/internal/cmdutil" @@ -19,7 +21,12 @@ func GetBranch(ctx context.Context, client *ps.Client, org, db string) (string, Database: db, }) if err != nil { - return "", err + switch cmdutil.ErrCode(err) { + case planetscale.ErrResponseMalformed: + return "", cmdutil.MalformedError(err) + default: + return "", errors.Wrap(err, "error listing branches") + } } if len(branches) == 0 {