Skip to content

Commit dd22cfd

Browse files
authored
refactor(base): allow positional argument override in delete (#1158)
This refactor allows positional argument overrides in delete commands. This allows us to delete multiple subresources (e.g. Storage Box Snapshots) at once.
1 parent daf4e61 commit dd22cfd

1 file changed

Lines changed: 55 additions & 7 deletions

File tree

internal/cmd/base/delete.go

Lines changed: 55 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package base
33
import (
44
"errors"
55
"fmt"
6+
"log"
67
"slices"
78
"strings"
89

@@ -22,25 +23,57 @@ type DeleteCmd struct {
2223
ShortDescription string
2324
NameSuggestions func(client hcapi2.Client) func() []string
2425
AdditionalFlags func(*cobra.Command)
25-
Fetch func(s state.State, cmd *cobra.Command, idOrName string) (interface{}, *hcloud.Response, error)
26+
Fetch FetchFunc
2627
Delete func(s state.State, cmd *cobra.Command, resource interface{}) (*hcloud.Action, error)
2728

29+
// FetchFunc is a factory function that produces [DeleteCmd.Fetch]. Should be set in case the resource has
30+
// more than a single identifier that is used in the positional arguments.
31+
// See [DeleteCmd.PositionalArgumentOverride].
32+
FetchFunc func(s state.State, cmd *cobra.Command, args []string) (FetchFunc, error)
33+
34+
// In case the resource does not have a single identifier that matches [DeleteCmd.ResourceNameSingular], this field
35+
// can be set to define the list of positional arguments.
36+
// For example, passing:
37+
// []string{"a", "b", "c"}
38+
// Would result in the usage string:
39+
// <a> <b> <c>...
40+
// Where c is are resources to be deleted.
41+
PositionalArgumentOverride []string
42+
43+
// Can be set if the default [DeleteCmd.NameSuggestions] is not enough. This is usually the case when
44+
// [DeleteCmd.FetchWithArgs] and [DeleteCmd.PositionalArgumentOverride] is being used.
45+
ValidArgsFunction func(client hcapi2.Client) []cobra.CompletionFunc
46+
2847
// Experimental is a function that will be used to mark the command as experimental.
2948
Experimental func(state.State, *cobra.Command) *cobra.Command
3049
}
3150

51+
type FetchFunc func(s state.State, cmd *cobra.Command, idOrName string) (any, *hcloud.Response, error)
52+
3253
// CobraCommand creates a command that can be registered with cobra.
3354
func (dc *DeleteCmd) CobraCommand(s state.State) *cobra.Command {
55+
var suggestArgs []cobra.CompletionFunc
56+
switch {
57+
case dc.NameSuggestions != nil:
58+
suggestArgs = append(suggestArgs,
59+
cmpl.SuggestCandidatesF(dc.NameSuggestions(s.Client())),
60+
)
61+
case dc.ValidArgsFunction != nil:
62+
suggestArgs = append(suggestArgs, dc.ValidArgsFunction(s.Client())...)
63+
default:
64+
log.Fatalf("delete command %s is missing ValidArgsFunction or NameSuggestions", dc.ResourceNameSingular)
65+
}
66+
3467
opts := ""
3568
if dc.AdditionalFlags != nil {
3669
opts = "[options] "
3770
}
3871

3972
cmd := &cobra.Command{
40-
Use: fmt.Sprintf("delete %s<%s>...", opts, util.ToKebabCase(dc.ResourceNameSingular)),
73+
Use: fmt.Sprintf("delete %s%s...", opts, positionalArguments(dc.ResourceNameSingular, dc.PositionalArgumentOverride)),
4174
Short: dc.ShortDescription,
4275
Args: util.Validate,
43-
ValidArgsFunction: cmpl.SuggestCandidatesF(dc.NameSuggestions(s.Client())),
76+
ValidArgsFunction: cmpl.SuggestArgs(suggestArgs...),
4477
TraverseChildren: true,
4578
DisableFlagsInUseLine: true,
4679
PreRunE: util.ChainRunE(s.EnsureToken),
@@ -62,17 +95,32 @@ const deleteBatchSize = 10
6295

6396
// Run executes a delete command.
6497
func (dc *DeleteCmd) Run(s state.State, cmd *cobra.Command, args []string) error {
65-
errs := make([]error, 0, len(args))
66-
deleted := make([]string, 0, len(args))
98+
toDelete := args[max(0, len(dc.PositionalArgumentOverride)-1):]
99+
100+
errs := make([]error, 0, len(toDelete))
101+
deleted := make([]string, 0, len(toDelete))
102+
103+
var (
104+
fetch FetchFunc
105+
err error
106+
)
107+
if dc.FetchFunc != nil {
108+
fetch, err = dc.FetchFunc(s, cmd, args)
109+
if err != nil {
110+
return err
111+
}
112+
} else {
113+
fetch = dc.Fetch
114+
}
67115

68-
for batch := range slices.Chunk(args, deleteBatchSize) {
116+
for batch := range slices.Chunk(toDelete, deleteBatchSize) {
69117
results := make([]util.ResourceState, len(batch))
70118
actions := make([]*hcloud.Action, 0, len(batch))
71119

72120
for i, idOrName := range batch {
73121
results[i] = util.ResourceState{IDOrName: idOrName}
74122

75-
resource, _, err := dc.Fetch(s, cmd, idOrName)
123+
resource, _, err := fetch(s, cmd, idOrName)
76124
if err != nil {
77125
results[i].Error = err
78126
continue

0 commit comments

Comments
 (0)