Skip to content

Commit 8566f03

Browse files
authored
render ea objects delete supports multiple objects (EA) (#283)
`render ea objects delete` now accepts multiple key arguments, allowing bulk deletion instead of requiring one invocation per object. Single-key usage is unchanged.
1 parent e3f9d85 commit 8566f03

2 files changed

Lines changed: 40 additions & 21 deletions

File tree

cmd/objectdelete.go

Lines changed: 30 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,15 @@ import (
1515
)
1616

1717
type ObjectDeleteInput struct {
18-
Key string `cli:"arg:0"`
18+
Keys []string
1919
Yes bool `cli:"yes"`
2020
Region string `cli:"region"`
2121
Local bool `cli:"local"`
2222
}
2323

2424
func (i *ObjectDeleteInput) Validate(interactive bool) error {
25-
if i.Key == "" {
26-
return fmt.Errorf("key is required")
25+
if len(i.Keys) == 0 {
26+
return fmt.Errorf("at least one key is required")
2727
}
2828
if i.Region == "" {
2929
i.Region = cfg.GetRegion()
@@ -35,9 +35,9 @@ func (i *ObjectDeleteInput) Validate(interactive bool) error {
3535
}
3636

3737
var objectDeleteCmd = &cobra.Command{
38-
Use: "delete <key>",
39-
Short: "Delete an object from storage",
40-
Long: `Delete an object from storage.
38+
Use: "delete <key> [key...]",
39+
Short: "Delete one or more objects from storage",
40+
Long: `Delete one or more objects from storage.
4141
4242
This operation is irreversible. By default, you will be prompted for confirmation
4343
unless you specify the --yes flag.
@@ -48,43 +48,44 @@ files are deleted from the .render/objects/ directory.
4848
Examples:
4949
render ea objects delete my/object/key --region=oregon
5050
render ea objects delete my/object/key --region=oregon --yes
51-
render ea objects delete uploads/data.json --region=oregon --yes
51+
render ea objects delete seed/file-00000.txt seed/file-00001.txt seed/file-00002.txt --region=oregon --yes
5252
render ea objects delete test/file --region=oregon --local --yes
5353
`,
54-
Args: cobra.ExactArgs(1),
54+
Args: cobra.MinimumNArgs(1),
5555
RunE: func(cmd *cobra.Command, args []string) error {
5656
var input ObjectDeleteInput
57+
input.Keys = args
5758

5859
if err := command.ParseCommand(cmd, args, &input); err != nil {
5960
return fmt.Errorf("failed to parse input: %w", err)
6061
}
6162

6263
// Prompt for confirmation unless --yes is specified or non-interactive
6364
if !input.Yes && command.IsInteractive(cmd.Context()) {
64-
if !confirmDelete(input.Key) {
65+
if !confirmDelete(input.Keys) {
6566
fmt.Println("Delete cancelled.")
6667
return nil
6768
}
6869
}
6970

70-
if nonInteractive, err := command.NonInteractive(cmd, func() (*storage.DeleteResult, error) {
71-
return deleteObject(cmd, input)
72-
}, text.ObjectDelete); err != nil {
71+
if nonInteractive, err := command.NonInteractive(cmd, func() ([]*storage.DeleteResult, error) {
72+
return deleteObjects(cmd, input)
73+
}, text.ObjectDeleteMultiple); err != nil {
7374
return err
7475
} else if nonInteractive {
7576
return nil
7677
}
7778

78-
result, err := deleteObject(cmd, input)
79+
results, err := deleteObjects(cmd, input)
7980
if err != nil {
8081
return err
8182
}
82-
fmt.Print(text.ObjectDelete(result))
83+
fmt.Print(text.ObjectDeleteMultiple(results))
8384
return nil
8485
},
8586
}
8687

87-
func deleteObject(cmd *cobra.Command, input ObjectDeleteInput) (*storage.DeleteResult, error) {
88+
func deleteObjects(cmd *cobra.Command, input ObjectDeleteInput) ([]*storage.DeleteResult, error) {
8889
ctx := cmd.Context()
8990

9091
cfg := storage.ServiceConfig{
@@ -97,17 +98,25 @@ func deleteObject(cmd *cobra.Command, input ObjectDeleteInput) (*storage.DeleteR
9798
return nil, fmt.Errorf("failed to create storage service: %w", err)
9899
}
99100

100-
result, err := svc.Delete(ctx, input.Key)
101-
if err != nil {
102-
return nil, err
101+
var results []*storage.DeleteResult
102+
for _, key := range input.Keys {
103+
result, err := svc.Delete(ctx, key)
104+
if err != nil {
105+
return nil, fmt.Errorf("failed to delete %s: %w", key, err)
106+
}
107+
results = append(results, result)
103108
}
104109

105-
return result, nil
110+
return results, nil
106111
}
107112

108-
func confirmDelete(key string) bool {
113+
func confirmDelete(keys []string) bool {
109114
reader := bufio.NewReader(os.Stdin)
110-
fmt.Printf("Are you sure you want to delete object '%s'? This cannot be undone. [y/N]: ", key)
115+
if len(keys) == 1 {
116+
fmt.Printf("Are you sure you want to delete object '%s'? This cannot be undone. [y/N]: ", keys[0])
117+
} else {
118+
fmt.Printf("Are you sure you want to delete %d objects? This cannot be undone. [y/N]: ", len(keys))
119+
}
111120
response, err := reader.ReadString('\n')
112121
if err != nil {
113122
return false

pkg/text/storage.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package text
22

33
import (
4+
"strings"
45
"time"
56

67
"github.com/jedib0t/go-pretty/table"
@@ -32,6 +33,15 @@ func ObjectDelete(result *storage.DeleteResult) string {
3233
return FormatStringF("Deleted %s", result.Key)
3334
}
3435

36+
// ObjectDeleteMultiple formats multiple delete results for text output
37+
func ObjectDeleteMultiple(results []*storage.DeleteResult) string {
38+
var lines []string
39+
for _, result := range results {
40+
lines = append(lines, FormatStringF("Deleted %s", result.Key))
41+
}
42+
return strings.Join(lines, "")
43+
}
44+
3545
// ObjectTable formats a list of objects for text output
3646
func ObjectTable(objects []storage.ObjectInfo) string {
3747
t := newTable()

0 commit comments

Comments
 (0)