Skip to content

Commit 23c18c3

Browse files
authored
Merge pull request #1072 from planetscale/password-delete-by-name
Add --name flag to password delete command
2 parents 55f8198 + 7975a6e commit 23c18c3

File tree

2 files changed

+210
-9
lines changed

2 files changed

+210
-9
lines changed

internal/cmd/password/delete.go

Lines changed: 93 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,29 +17,100 @@ import (
1717

1818
func DeleteCmd(ch *cmdutil.Helper) *cobra.Command {
1919
var force bool
20+
var name string
2021

2122
cmd := &cobra.Command{
22-
Use: "delete <database> <branch> <password-id>",
23+
Use: "delete <database> <branch> [<password-id>]",
2324
Short: "Delete a branch password",
24-
Args: cmdutil.RequiredArgs("database", "branch", "password-id"),
25+
Args: cobra.RangeArgs(2, 3),
2526
Aliases: []string{"rm"},
2627
RunE: func(cmd *cobra.Command, args []string) error {
2728
ctx := cmd.Context()
2829
database := args[0]
2930
branch := args[1]
30-
passwordId := args[2]
31-
31+
32+
// Validate that either password-id is provided as arg or --name flag is used
33+
var passwordId string
34+
if name != "" && len(args) == 3 {
35+
return errors.New("cannot specify both password-id argument and --name flag")
36+
}
37+
if name == "" && len(args) != 3 {
38+
return errors.New("must provide either password-id argument or --name flag")
39+
}
40+
3241
client, err := ch.Client()
3342
if err != nil {
3443
return err
3544
}
45+
46+
// If using --name flag, find the password by name
47+
if name != "" {
48+
end := ch.Printer.PrintProgress(fmt.Sprintf("Finding password %s in %s/%s",
49+
printer.BoldBlue(name), printer.BoldBlue(database), printer.BoldBlue(branch)))
50+
51+
// Fetch all passwords to find the one with matching name
52+
var allPasswords []*ps.DatabaseBranchPassword
53+
page := 1
54+
perPage := 100
55+
56+
for {
57+
passwords, err := client.Passwords.List(ctx, &ps.ListDatabaseBranchPasswordRequest{
58+
Organization: ch.Config.Organization,
59+
Database: database,
60+
Branch: branch,
61+
}, ps.WithPage(page), ps.WithPerPage(perPage))
62+
if err != nil {
63+
end()
64+
switch cmdutil.ErrCode(err) {
65+
case ps.ErrNotFound:
66+
return fmt.Errorf("branch %s does not exist in database %s (organization: %s)",
67+
printer.BoldBlue(branch), printer.BoldBlue(database), printer.BoldBlue(ch.Config.Organization))
68+
default:
69+
return cmdutil.HandleError(err)
70+
}
71+
}
72+
73+
allPasswords = append(allPasswords, passwords...)
74+
75+
// Check if there are more pages
76+
if len(passwords) < perPage {
77+
break
78+
}
79+
page++
80+
}
81+
82+
end()
83+
84+
// Find password with matching name
85+
var foundPassword *ps.DatabaseBranchPassword
86+
for _, password := range allPasswords {
87+
if password.Name == name {
88+
foundPassword = password
89+
break
90+
}
91+
}
92+
93+
if foundPassword == nil {
94+
return fmt.Errorf("password with name %s does not exist in branch %s of %s (organization: %s)",
95+
printer.BoldBlue(name), printer.BoldBlue(branch), printer.BoldBlue(database), printer.BoldBlue(ch.Config.Organization))
96+
}
97+
98+
passwordId = foundPassword.PublicID
99+
} else {
100+
passwordId = args[2]
101+
}
36102

37103
if !force {
38104
if ch.Printer.Format() != printer.Human {
39105
return fmt.Errorf("cannot delete password with the output format %q (run with -force to override)", ch.Printer.Format())
40106
}
41107

42-
confirmationName := fmt.Sprintf("%s/%s/%s", database, branch, passwordId)
108+
var confirmationName string
109+
if name != "" {
110+
confirmationName = fmt.Sprintf("%s/%s/%s", database, branch, name)
111+
} else {
112+
confirmationName = fmt.Sprintf("%s/%s/%s", database, branch, passwordId)
113+
}
43114
if !printer.IsTTY {
44115
return fmt.Errorf("cannot confirm deletion of password %q (run with -force to override)", confirmationName)
45116
}
@@ -66,8 +137,15 @@ func DeleteCmd(ch *cmdutil.Helper) *cobra.Command {
66137
}
67138
}
68139

69-
end := ch.Printer.PrintProgress(fmt.Sprintf("Deleting password %s from %s/%s",
70-
printer.BoldBlue(passwordId), printer.BoldBlue(database), printer.BoldBlue(branch)))
140+
var deleteMsg string
141+
if name != "" {
142+
deleteMsg = fmt.Sprintf("Deleting password %s from %s/%s",
143+
printer.BoldBlue(name), printer.BoldBlue(database), printer.BoldBlue(branch))
144+
} else {
145+
deleteMsg = fmt.Sprintf("Deleting password %s from %s/%s",
146+
printer.BoldBlue(passwordId), printer.BoldBlue(database), printer.BoldBlue(branch))
147+
}
148+
end := ch.Printer.PrintProgress(deleteMsg)
71149
defer end()
72150

73151
err = client.Passwords.Delete(ctx, &ps.DeleteDatabaseBranchPasswordRequest{
@@ -89,8 +167,13 @@ func DeleteCmd(ch *cmdutil.Helper) *cobra.Command {
89167
end()
90168

91169
if ch.Printer.Format() == printer.Human {
92-
ch.Printer.Printf("Password %s was successfully deleted from %s.\n",
93-
printer.BoldBlue(passwordId), printer.BoldBlue(branch))
170+
if name != "" {
171+
ch.Printer.Printf("Password %s was successfully deleted from %s.\n",
172+
printer.BoldBlue(name), printer.BoldBlue(branch))
173+
} else {
174+
ch.Printer.Printf("Password %s was successfully deleted from %s.\n",
175+
printer.BoldBlue(passwordId), printer.BoldBlue(branch))
176+
}
94177
return nil
95178
}
96179

@@ -105,5 +188,6 @@ func DeleteCmd(ch *cmdutil.Helper) *cobra.Command {
105188
}
106189

107190
cmd.Flags().BoolVar(&force, "force", false, "Delete a password without confirmation")
191+
cmd.Flags().StringVar(&name, "name", "", "Delete password by name instead of ID")
108192
return cmd
109193
}

internal/cmd/password/delete_test.go

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,3 +64,120 @@ func TestPassword_DeleteCmd(t *testing.T) {
6464
}
6565
c.Assert(buf.String(), qt.JSONEquals, res)
6666
}
67+
68+
func TestPassword_DeleteCmdByName(t *testing.T) {
69+
c := qt.New(t)
70+
71+
var buf bytes.Buffer
72+
format := printer.JSON
73+
p := printer.NewPrinter(&format)
74+
p.SetResourceOutput(&buf)
75+
76+
org := "planetscale"
77+
db := "planetscale"
78+
branch := "development"
79+
passwordName := "test-password"
80+
passwordId := "password123"
81+
82+
svc := &mock.PasswordsService{
83+
ListFn: func(ctx context.Context, req *ps.ListDatabaseBranchPasswordRequest, opts ...ps.ListOption) ([]*ps.DatabaseBranchPassword, error) {
84+
c.Assert(req.Organization, qt.Equals, org)
85+
c.Assert(req.Database, qt.Equals, db)
86+
c.Assert(req.Branch, qt.Equals, branch)
87+
88+
return []*ps.DatabaseBranchPassword{
89+
{
90+
PublicID: passwordId,
91+
Name: passwordName,
92+
},
93+
{
94+
PublicID: "other-id",
95+
Name: "other-password",
96+
},
97+
}, nil
98+
},
99+
DeleteFn: func(ctx context.Context, req *ps.DeleteDatabaseBranchPasswordRequest) error {
100+
c.Assert(req.Organization, qt.Equals, org)
101+
c.Assert(req.Database, qt.Equals, db)
102+
c.Assert(req.Branch, qt.Equals, branch)
103+
c.Assert(req.PasswordId, qt.Equals, passwordId)
104+
105+
return nil
106+
},
107+
}
108+
109+
ch := &cmdutil.Helper{
110+
Printer: p,
111+
Config: &config.Config{
112+
Organization: org,
113+
},
114+
Client: func() (*ps.Client, error) {
115+
return &ps.Client{
116+
Passwords: svc,
117+
}, nil
118+
},
119+
}
120+
121+
cmd := DeleteCmd(ch)
122+
cmd.SetArgs([]string{db, branch, "--name", passwordName, "--force"})
123+
err := cmd.Execute()
124+
125+
c.Assert(err, qt.IsNil)
126+
c.Assert(svc.ListFnInvoked, qt.IsTrue)
127+
c.Assert(svc.DeleteFnInvoked, qt.IsTrue)
128+
129+
res := map[string]string{
130+
"result": "password deleted",
131+
"password_id": passwordId,
132+
"branch": branch,
133+
}
134+
c.Assert(buf.String(), qt.JSONEquals, res)
135+
}
136+
137+
func TestPassword_DeleteCmdByNameNotFound(t *testing.T) {
138+
c := qt.New(t)
139+
140+
var buf bytes.Buffer
141+
format := printer.JSON
142+
p := printer.NewPrinter(&format)
143+
p.SetResourceOutput(&buf)
144+
145+
org := "planetscale"
146+
db := "planetscale"
147+
branch := "development"
148+
passwordName := "nonexistent-password"
149+
150+
svc := &mock.PasswordsService{
151+
ListFn: func(ctx context.Context, req *ps.ListDatabaseBranchPasswordRequest, opts ...ps.ListOption) ([]*ps.DatabaseBranchPassword, error) {
152+
c.Assert(req.Organization, qt.Equals, org)
153+
c.Assert(req.Database, qt.Equals, db)
154+
c.Assert(req.Branch, qt.Equals, branch)
155+
156+
return []*ps.DatabaseBranchPassword{
157+
{
158+
PublicID: "other-id",
159+
Name: "other-password",
160+
},
161+
}, nil
162+
},
163+
}
164+
165+
ch := &cmdutil.Helper{
166+
Printer: p,
167+
Config: &config.Config{
168+
Organization: org,
169+
},
170+
Client: func() (*ps.Client, error) {
171+
return &ps.Client{
172+
Passwords: svc,
173+
}, nil
174+
},
175+
}
176+
177+
cmd := DeleteCmd(ch)
178+
cmd.SetArgs([]string{db, branch, "--name", passwordName, "--force"})
179+
err := cmd.Execute()
180+
181+
c.Assert(err, qt.ErrorMatches, `password with name nonexistent-password does not exist.*`)
182+
c.Assert(svc.ListFnInvoked, qt.IsTrue)
183+
}

0 commit comments

Comments
 (0)