forked from profclems/glab
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feature: add variable delete command (profclems#890)
* Move IsValidKey to new variableutils package * add variable delete command * remove unnecessary alias * wip: add tests * working tests
- Loading branch information
Showing
9 changed files
with
387 additions
and
58 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
package delete | ||
|
||
import ( | ||
"fmt" | ||
|
||
"errors" | ||
|
||
"github.com/MakeNowJust/heredoc" | ||
"github.com/profclems/glab/api" | ||
"github.com/profclems/glab/commands/cmdutils" | ||
"github.com/profclems/glab/commands/variable/variableutils" | ||
"github.com/profclems/glab/internal/glrepo" | ||
"github.com/profclems/glab/pkg/iostreams" | ||
|
||
"github.com/spf13/cobra" | ||
"github.com/xanzy/go-gitlab" | ||
) | ||
|
||
type DeleteOpts struct { | ||
HTTPClient func() (*gitlab.Client, error) | ||
IO *iostreams.IOStreams | ||
BaseRepo func() (glrepo.Interface, error) | ||
|
||
Key string | ||
Scope string | ||
Group string | ||
} | ||
|
||
func NewCmdSet(f *cmdutils.Factory, runE func(opts *DeleteOpts) error) *cobra.Command { | ||
opts := &DeleteOpts{ | ||
IO: f.IO, | ||
} | ||
|
||
cmd := &cobra.Command{ | ||
Use: "delete <key>", | ||
Short: "Delete a project or group variable", | ||
Aliases: []string{"remove"}, | ||
Args: cobra.ExactArgs(1), | ||
Example: heredoc.Doc(` | ||
$ glab variable delete VAR_NAME | ||
$ glab variable delete VAR_NAME --scope=prod | ||
$ glab variable delete VARNAME -g mygroup | ||
`), | ||
RunE: func(cmd *cobra.Command, args []string) (err error) { | ||
opts.HTTPClient = f.HttpClient | ||
opts.BaseRepo = f.BaseRepo | ||
opts.Key = args[0] | ||
|
||
if !variableutils.IsValidKey(opts.Key) { | ||
err = cmdutils.FlagError{Err: fmt.Errorf("invalid key provided.\n%s", variableutils.ValidKeyMsg)} | ||
return | ||
} else if len(args) != 1 { | ||
err = cmdutils.FlagError{Err: errors.New("no key provided")} | ||
} | ||
|
||
if cmd.Flags().Changed("scope") && opts.Group != "" { | ||
err = cmdutils.FlagError{Err: errors.New("scope is not required for group variables")} | ||
return | ||
} | ||
|
||
if runE != nil { | ||
err = runE(opts) | ||
return | ||
} | ||
err = deleteRun(opts) | ||
return | ||
}, | ||
} | ||
|
||
cmd.Flags().StringVarP(&opts.Scope, "scope", "s", "*", "The environment_scope of the variable. All (*), or specific environments") | ||
cmd.Flags().StringVarP(&opts.Group, "group", "g", "", "Delete variable from a group") | ||
|
||
return cmd | ||
|
||
} | ||
|
||
func deleteRun(opts *DeleteOpts) error { | ||
c := opts.IO.Color() | ||
httpClient, err := opts.HTTPClient() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
baseRepo, err := opts.BaseRepo() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if opts.Group == "" { | ||
// Delete project-level variable | ||
err = api.DeleteProjectVariable(httpClient, baseRepo.FullName(), opts.Key, opts.Scope) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
fmt.Fprintf(opts.IO.StdOut, "%s Deleted variable %s with scope %s for %s\n", c.GreenCheck(), opts.Key, opts.Scope, baseRepo.FullName()) | ||
} else { | ||
// Delete group-level variable | ||
err = api.DeleteGroupVariable(httpClient, opts.Group, opts.Key) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
fmt.Fprintf(opts.IO.StdOut, "%s Deleted variable %s for group %s\n", c.GreenCheck(), opts.Key, opts.Group) | ||
} | ||
|
||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,171 @@ | ||
package delete | ||
|
||
import ( | ||
"bytes" | ||
"net/http" | ||
"testing" | ||
|
||
"github.com/profclems/glab/api" | ||
"github.com/profclems/glab/internal/glrepo" | ||
"github.com/profclems/glab/pkg/iostreams" | ||
"github.com/xanzy/go-gitlab" | ||
|
||
"github.com/alecthomas/assert" | ||
"github.com/google/shlex" | ||
"github.com/profclems/glab/commands/cmdutils" | ||
"github.com/profclems/glab/pkg/httpmock" | ||
) | ||
|
||
func Test_NewCmdSet(t *testing.T) { | ||
tests := []struct { | ||
name string | ||
cli string | ||
wants DeleteOpts | ||
stdinTTY bool | ||
wantsErr bool | ||
}{ | ||
{ | ||
name: "delete var", | ||
cli: "cool_secret", | ||
wantsErr: false, | ||
}, | ||
{ | ||
name: "delete scoped var", | ||
cli: "cool_secret --scope prod", | ||
wantsErr: false, | ||
}, | ||
{ | ||
name: "delete group var", | ||
cli: "cool_secret -g mygroup", | ||
wantsErr: false, | ||
}, | ||
{ | ||
name: "delete scoped group var", | ||
cli: "cool_secret -g mygroup --scope prod", | ||
wantsErr: true, | ||
}, | ||
{ | ||
name: "no name", | ||
cli: "", | ||
wantsErr: true, | ||
}, | ||
{ | ||
name: "invalid characters in name", | ||
cli: "BAD-SECRET", | ||
wantsErr: true, | ||
}, | ||
} | ||
|
||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
io, _, _, _ := iostreams.Test() | ||
f := &cmdutils.Factory{ | ||
IO: io, | ||
} | ||
|
||
io.IsInTTY = tt.stdinTTY | ||
|
||
argv, err := shlex.Split(tt.cli) | ||
assert.NoError(t, err) | ||
|
||
cmd := NewCmdSet(f, func(opts *DeleteOpts) error { | ||
return nil | ||
}) | ||
|
||
cmd.SetArgs(argv) | ||
cmd.SetIn(&bytes.Buffer{}) | ||
cmd.SetOut(&bytes.Buffer{}) | ||
cmd.SetErr(&bytes.Buffer{}) | ||
|
||
_, err = cmd.ExecuteC() | ||
if tt.wantsErr { | ||
assert.Error(t, err) | ||
return | ||
} | ||
assert.NoError(t, err) | ||
}) | ||
} | ||
} | ||
|
||
func Test_deleteRun(t *testing.T) { | ||
reg := &httpmock.Mocker{ | ||
MatchURL: httpmock.PathAndQuerystring, | ||
} | ||
defer reg.Verify(t) | ||
|
||
reg.RegisterResponder("DELETE", "/api/v4/projects/owner%2Frepo/variables/TEST_VAR?filter%5Benvironment_scope%5D=%2A", | ||
httpmock.NewStringResponse(204, " "), | ||
) | ||
|
||
reg.RegisterResponder("DELETE", "/api/v4/projects/owner%2Frepo/variables/TEST_VAR?filter%5Benvironment_scope%5D=stage", | ||
httpmock.NewStringResponse(204, " "), | ||
) | ||
|
||
reg.RegisterResponder("DELETE", "/api/v4/groups/testGroup/variables/TEST_VAR", | ||
httpmock.NewStringResponse(204, " "), | ||
) | ||
|
||
var httpClient = func() (*gitlab.Client, error) { | ||
a, _ := api.TestClient(&http.Client{Transport: reg}, "", "gitlab.com", false) | ||
return a.Lab(), nil | ||
} | ||
var baseRepo = func() (glrepo.Interface, error) { | ||
return glrepo.FromFullName("owner/repo") | ||
} | ||
|
||
tests := []struct { | ||
name string | ||
opts DeleteOpts | ||
wantsErr bool | ||
wantsOutput string | ||
}{ | ||
{ | ||
name: "delete project variable no scope", | ||
opts: DeleteOpts{ | ||
HTTPClient: httpClient, | ||
BaseRepo: baseRepo, | ||
Key: "TEST_VAR", | ||
Scope: "*", | ||
}, | ||
wantsErr: false, | ||
wantsOutput: "✓ Deleted variable TEST_VAR with scope * for owner/repo\n", | ||
}, | ||
{ | ||
name: "delete project variable with stage scope", | ||
opts: DeleteOpts{ | ||
HTTPClient: httpClient, | ||
BaseRepo: baseRepo, | ||
Key: "TEST_VAR", | ||
Scope: "stage", | ||
}, | ||
wantsErr: false, | ||
wantsOutput: "✓ Deleted variable TEST_VAR with scope stage for owner/repo\n", | ||
}, | ||
{ | ||
name: "delete group variable", | ||
opts: DeleteOpts{ | ||
HTTPClient: httpClient, | ||
BaseRepo: baseRepo, | ||
Key: "TEST_VAR", | ||
Scope: "", | ||
Group: "testGroup", | ||
}, | ||
wantsErr: false, | ||
wantsOutput: "✓ Deleted variable TEST_VAR for group testGroup\n", | ||
}, | ||
} | ||
|
||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
_, _ = tt.opts.HTTPClient() | ||
|
||
io, _, stdout, _ := iostreams.Test() | ||
tt.opts.IO = io | ||
io.IsInTTY = false | ||
|
||
err := deleteRun(&tt.opts) | ||
assert.NoError(t, err) | ||
assert.Equal(t, stdout.String(), tt.wantsOutput) | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.