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

[Fixs #109] Adding confirmation on delete actions #148

Merged
merged 5 commits into from
Oct 10, 2016
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
3 changes: 3 additions & 0 deletions args.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,4 +122,7 @@ const (
ArgVolumeRegion = "region"
// ArgVolumeList is the IDs of many volumes.
ArgVolumeList = "volumes"

// ArgDeleteForce forces deletion actions
ArgDeleteForce = "force"
)
30 changes: 30 additions & 0 deletions commands/confirmation.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package commands

import (
"bufio"
"fmt"
"os"
"strings"
)

var retrieveUserInput = func(message string) (string, error) {
reader := bufio.NewReader(os.Stdin)
warnConfirm("Are you sure you want to " + message + " (y/N) ? ")
answer, err := reader.ReadString('\n')
if err != nil {
return "", err
}
return strings.ToLower(strings.Replace(answer, "\n", "", 1)), nil
}

func AskForConfirm(message string) error {
answer, err := retrieveUserInput(message)
if err != nil {
return fmt.Errorf("unable to parse users input: %s", err)
}
if answer == "y" || answer == "ye" || answer == "yes" {
return nil
} else {
return fmt.Errorf("invaild user input")
}
}
64 changes: 64 additions & 0 deletions commands/confirmation_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package commands

import (
"fmt"
"testing"

"github.com/stretchr/testify/assert"
)

func TestAskForConfirmYes(t *testing.T) {
rui := retrieveUserInput
defer func() {
retrieveUserInput = rui
}()

retrieveUserInput = func(string) (string, error) {
return "yes", nil
}

err := AskForConfirm("test")
assert.NoError(t, err)
}

func TestAskForConfirmNo(t *testing.T) {
rui := retrieveUserInput
defer func() {
retrieveUserInput = rui
}()

retrieveUserInput = func(string) (string, error) {
return "no", nil
}

err := AskForConfirm("test")
assert.Error(t, err)
}

func TestAskForConfirmAny(t *testing.T) {
rui := retrieveUserInput
defer func() {
retrieveUserInput = rui
}()

retrieveUserInput = func(string) (string, error) {
return "some-random-message", nil
}

err := AskForConfirm("test")
assert.Error(t, err)
}

func TestAskForConfirmError(t *testing.T) {
rui := retrieveUserInput
defer func() {
retrieveUserInput = rui
}()

retrieveUserInput = func(string) (string, error) {
return "", fmt.Errorf("test-error")
}

err := AskForConfirm("test")
assert.Error(t, err)
}
37 changes: 27 additions & 10 deletions commands/droplets.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,9 @@ func Droplet() *Command {

AddStringSliceFlag(cmdDropletCreate, doctl.ArgVolumeList, []string{}, "Volumes to attach")

CmdBuilder(cmd, RunDropletDelete, "delete ID [ID|Name ...]", "Delete droplet by id or name", Writer,
cmdRunDropletDelete := CmdBuilder(cmd, RunDropletDelete, "delete ID [ID|Name ...]", "Delete droplet by id or name", Writer,
aliasOpt("d", "del", "rm"), docCategories("droplet"))
AddBoolFlag(cmdRunDropletDelete, doctl.ArgDeleteForce, false, "Force droplet delete")

CmdBuilder(cmd, RunDropletGet, "get", "get droplet", Writer,
aliasOpt("g"), displayerType(&droplet{}), docCategories("droplet"))
Expand Down Expand Up @@ -267,7 +268,7 @@ func RunDropletCreate(c *CmdConfig) error {

wg.Wait()
close(errs)

item := &droplet{droplets: createdList}
c.Display(item)

Expand Down Expand Up @@ -424,6 +425,11 @@ func allInt(in []string) ([]int, error) {
func RunDropletDelete(c *CmdConfig) error {
ds := c.Droplets()

force, err := c.Doit.GetBool(c.NS, doctl.ArgDeleteForce)
if err != nil {
return err
}

tagName, err := c.Doit.GetString(c.NS, doctl.ArgTagName)
if err != nil {
return err
Expand All @@ -434,20 +440,31 @@ func RunDropletDelete(c *CmdConfig) error {
} else if len(c.Args) > 0 && tagName != "" {
return fmt.Errorf("please specify droplets identifiers or a tag name")
} else if tagName != "" {
return ds.DeleteByTag(tagName)
if force || AskForConfirm("delete droplet by \""+tagName+"\" tag") == nil {
return ds.DeleteByTag(tagName)
} else {
return fmt.Errorf("Operation aborted.")
}
return nil
}

fn := func(ids []int) error {
for _, id := range ids {
if err := ds.Delete(id); err != nil {
return fmt.Errorf("unable to delete droplet %d: %v", id, err)
if force || AskForConfirm("delete droplet(s)") == nil {

fn := func(ids []int) error {
for _, id := range ids {
if err := ds.Delete(id); err != nil {
return fmt.Errorf("unable to delete droplet %d: %v", id, err)
}
}
return nil
}

return nil
return matchDroplets(c.Args, ds, fn)
} else {
return fmt.Errorf("Operation aborted.")
}

return matchDroplets(c.Args, ds, fn)
return nil

}

type matchDropletsFn func(ids []int) error
Expand Down
7 changes: 7 additions & 0 deletions commands/droplets_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,9 +141,11 @@ func TestDropletDelete(t *testing.T) {
tm.droplets.On("Delete", 1).Return(nil)

config.Args = append(config.Args, strconv.Itoa(testDroplet.ID))
config.Doit.Set(config.NS, doctl.ArgDeleteForce, true)

err := RunDropletDelete(config)
assert.NoError(t, err)

})
}

Expand All @@ -152,6 +154,7 @@ func TestDropletDeleteByTag(t *testing.T) {
tm.droplets.On("DeleteByTag", "my-tag").Return(nil)

config.Doit.Set(config.NS, doctl.ArgTagName, "my-tag")
config.Doit.Set(config.NS, doctl.ArgDeleteForce, true)

err := RunDropletDelete(config)
assert.NoError(t, err)
Expand All @@ -165,6 +168,7 @@ func TestDropletDeleteRepeatedID(t *testing.T) {

id := strconv.Itoa(testDroplet.ID)
config.Args = append(config.Args, id, id)
config.Doit.Set(config.NS, doctl.ArgDeleteForce, true)

err := RunDropletDelete(config)
assert.NoError(t, err)
Expand All @@ -177,6 +181,7 @@ func TestDropletDeleteByName(t *testing.T) {
tm.droplets.On("Delete", 1).Return(nil)

config.Args = append(config.Args, testDroplet.Name)
config.Doit.Set(config.NS, doctl.ArgDeleteForce, true)

err := RunDropletDelete(config)
assert.NoError(t, err)
Expand All @@ -189,6 +194,7 @@ func TestDropletDeleteByName_Ambiguous(t *testing.T) {
tm.droplets.On("List").Return(list, nil)

config.Args = append(config.Args, testDroplet.Name)
config.Doit.Set(config.NS, doctl.ArgDeleteForce, true)

err := RunDropletDelete(config)
t.Log(err)
Expand All @@ -203,6 +209,7 @@ func TestDropletDelete_MixedNameAndType(t *testing.T) {

id := strconv.Itoa(testDroplet.ID)
config.Args = append(config.Args, id, testDroplet.Name)
config.Doit.Set(config.NS, doctl.ArgDeleteForce, true)

err := RunDropletDelete(config)
assert.NoError(t, err)
Expand Down
3 changes: 3 additions & 0 deletions commands/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@ func checkErr(err error, cmd ...*cobra.Command) {
func warn(msg string) {
fmt.Fprintf(color.Output, "%s: %s\n\n", colorWarn, msg)
}
func warnConfirm(msg string) {
fmt.Fprintf(color.Output, "%s: %s", colorWarn, msg)
}

func notice(msg string) {
fmt.Fprintf(color.Output, "%s: %s\n\n", colorNotice, msg)
Expand Down