Skip to content
This repository has been archived by the owner on Feb 16, 2023. It is now read-only.

Commit

Permalink
Merge pull request #290 from secrethub/feature/mkdir-multiple-directo…
Browse files Browse the repository at this point in the history
…ry-args

Support multiple arguments for mkdir command
  • Loading branch information
jpcoenen authored Jun 9, 2020
2 parents b393f49 + 68d3efb commit 49f23fb
Show file tree
Hide file tree
Showing 3 changed files with 180 additions and 27 deletions.
58 changes: 41 additions & 17 deletions internals/secrethub/mkdir.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ package secrethub

import (
"fmt"
"os"

"github.com/secrethub/secrethub-go/internals/api"
"github.com/secrethub/secrethub-go/pkg/secrethub"

"github.com/secrethub/secrethub-cli/internals/cli/ui"
"github.com/secrethub/secrethub-cli/internals/secrethub/command"

"github.com/secrethub/secrethub-go/internals/api"
)

// Errors
Expand All @@ -17,7 +19,7 @@ var (
// MkDirCommand creates a new directory inside a repository.
type MkDirCommand struct {
io ui.IO
path api.DirPath
paths dirPathList
parents bool
newClient newClientFunc
}
Expand All @@ -33,36 +35,58 @@ func NewMkDirCommand(io ui.IO, newClient newClientFunc) *MkDirCommand {
// Register registers the command, arguments and flags on the provided Registerer.
func (cmd *MkDirCommand) Register(r command.Registerer) {
clause := r.Command("mkdir", "Create a new directory.")
clause.Arg("dir-path", "The path to the directory").Required().PlaceHolder(dirPathPlaceHolder).SetValue(&cmd.path)
clause.Arg("dir-paths", "The paths to the directories").Required().PlaceHolder(dirPathsPlaceHolder).SetValue(&cmd.paths)
clause.Flag("parents", "Create parent directories if needed. Does not error when directories already exist.").BoolVar(&cmd.parents)

command.BindAction(clause, cmd.Run)
}

// Run executes the command.
func (cmd *MkDirCommand) Run() error {
if cmd.path.IsRepoPath() {
return ErrMkDirOnRootDir
}

client, err := cmd.newClient()
if err != nil {
return err
}

if cmd.parents {
err = client.Dirs().CreateAll(cmd.path.Value())
if err != nil {
return err
}
} else {
_, err = client.Dirs().Create(cmd.path.Value())
for _, path := range cmd.paths {
err := cmd.createDirectory(client, path)
if err != nil {
return err
fmt.Fprintf(os.Stderr, "Could not create a new directory at %s: %s\n", path, err)
} else {
fmt.Fprintf(cmd.io.Output(), "Created a new directory at %s\n", path)
}
}
return nil
}

fmt.Fprintf(cmd.io.Output(), "Created a new directory at %s\n", cmd.path)
// createDirectory validates the given path and creates a directory on it.
func (cmd *MkDirCommand) createDirectory(client secrethub.ClientInterface, path string) error {
dirPath, err := api.NewDirPath(path)
if err != nil {
return err
}
if dirPath.IsRepoPath() {
return ErrMkDirOnRootDir
}
if cmd.parents {
return client.Dirs().CreateAll(dirPath.Value())
}
_, err = client.Dirs().Create(dirPath.Value())
return err
}

// dirPathList represents the value of a repeatable directory path argument.
type dirPathList []string

func (d *dirPathList) String() string {
return ""
}

func (d *dirPathList) Set(path string) error {
*d = append(*d, path)
return nil
}

func (d *dirPathList) IsCumulative() bool {
return true
}
148 changes: 138 additions & 10 deletions internals/secrethub/mkdir_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@ import (

func TestMkDirCommand(t *testing.T) {
cases := map[string]struct {
path string
paths []string
newClient func() (secrethub.ClientInterface, error)
stdout string
err error
}{
"success": {
path: "namespace/repo/dir",
paths: []string{"namespace/repo/dir"},
newClient: func() (secrethub.ClientInterface, error) {
return fakeclient.Client{
DirService: &fakeclient.DirService{
Expand All @@ -42,21 +42,37 @@ func TestMkDirCommand(t *testing.T) {
stdout: "Created a new directory at namespace/repo/dir\n",
err: nil,
},
"on root dir": {
path: "namespace/repo",
stdout: "",
err: ErrMkDirOnRootDir,
"success multiple dirs": {
paths: []string{"namespace/repo/dir1", "namespace/repo/dir2"},
newClient: func() (secrethub.ClientInterface, error) {
return fakeclient.Client{
DirService: &fakeclient.DirService{
CreateFunc: func(path string) (*api.Dir, error) {
return &api.Dir{
DirID: uuid.New(),
BlindName: "blindname",
Name: "dir",
Status: api.StatusOK,
CreatedAt: time.Now().UTC(),
LastModifiedAt: time.Now().UTC(),
}, nil
},
},
}, nil
},
stdout: "Created a new directory at namespace/repo/dir1\nCreated a new directory at namespace/repo/dir2\n",
err: nil,
},
"new client fails": {
path: "namespace/repo/dir",
paths: []string{"namespace/repo/dir"},
newClient: func() (secrethub.ClientInterface, error) {
return nil, errio.Namespace("test").Code("foo").Error("bar")
},
stdout: "",
err: errio.Namespace("test").Code("foo").Error("bar"),
},
"create dir fails": {
path: "namespace/repo/dir",
paths: []string{"namespace/repo/dir"},
newClient: func() (secrethub.ClientInterface, error) {
return fakeclient.Client{
DirService: &fakeclient.DirService{
Expand All @@ -67,16 +83,65 @@ func TestMkDirCommand(t *testing.T) {
}, nil
},
stdout: "",
err: api.ErrDirAlreadyExists,
},
"create dir fails on second dir": {
paths: []string{"namespace/repo/dir1", "namespace/repo/dir2"},
newClient: func() (secrethub.ClientInterface, error) {
return fakeclient.Client{
DirService: &fakeclient.DirService{
CreateFunc: func(path string) (*api.Dir, error) {
if path == "namespace/repo/dir2" {
return nil, api.ErrDirAlreadyExists
}
return &api.Dir{
DirID: uuid.New(),
BlindName: "blindname",
Name: "dir",
Status: api.StatusOK,
CreatedAt: time.Now().UTC(),
LastModifiedAt: time.Now().UTC(),
}, nil
},
},
}, nil
},
stdout: "Created a new directory at namespace/repo/dir1\n",
},
"create dir fails on first dir": {
paths: []string{"namespace/repo/dir1", "namespace/repo/dir2"},
newClient: func() (secrethub.ClientInterface, error) {
return fakeclient.Client{
DirService: &fakeclient.DirService{
CreateFunc: func(path string) (*api.Dir, error) {
if path == "namespace/repo/dir1" {
return nil, api.ErrDirAlreadyExists
}
return &api.Dir{
DirID: uuid.New(),
BlindName: "blindname",
Name: "dir",
Status: api.StatusOK,
CreatedAt: time.Now().UTC(),
LastModifiedAt: time.Now().UTC(),
}, nil
},
},
}, nil
},
stdout: "Created a new directory at namespace/repo/dir2\n",
},
}

for name, tc := range cases {
t.Run(name, func(t *testing.T) {
io := fakeui.NewIO(t)
dirPaths := dirPathList{}
for _, path := range tc.paths {
_ = dirPaths.Set(path)
}
cmd := MkDirCommand{
io: io,
path: api.DirPath(tc.path),
paths: dirPaths,
newClient: tc.newClient,
}

Expand All @@ -87,3 +152,66 @@ func TestMkDirCommand(t *testing.T) {
})
}
}

func TestCreateDirectory(t *testing.T) {
cases := map[string]struct {
client secrethub.ClientInterface
path string
err error
}{
"success": {
client: fakeclient.Client{
DirService: &fakeclient.DirService{
CreateFunc: func(path string) (*api.Dir, error) {
return &api.Dir{
DirID: uuid.New(),
BlindName: "blindname",
Name: "dir",
Status: api.StatusOK,
CreatedAt: time.Now().UTC(),
LastModifiedAt: time.Now().UTC(),
}, nil
},
},
},
path: "namespace/repo/dir",
},
"root dir": {
client: fakeclient.Client{
DirService: &fakeclient.DirService{
CreateFunc: func(path string) (*api.Dir, error) {
return &api.Dir{
DirID: uuid.New(),
BlindName: "blindname",
Name: "dir",
Status: api.StatusOK,
CreatedAt: time.Now().UTC(),
LastModifiedAt: time.Now().UTC(),
}, nil
},
},
},
path: "namespace/repo",
err: ErrMkDirOnRootDir,
},
"create dir fails": {
client: fakeclient.Client{
DirService: &fakeclient.DirService{
CreateFunc: func(path string) (*api.Dir, error) {
return nil, api.ErrDirAlreadyExists
},
},
},
path: "namespace/repo/dir",
err: api.ErrDirAlreadyExists,
},
}

for name, tc := range cases {
t.Run(name, func(t *testing.T) {
cmd := MkDirCommand{}
err := cmd.createDirectory(tc.client, tc.path)
assert.Equal(t, err, tc.err)
})
}
}
1 change: 1 addition & 0 deletions internals/secrethub/path_placeholders.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package secrethub
const (
repoPathPlaceHolder = "<namespace>/<repo>"
dirPathPlaceHolder = repoPathPlaceHolder + "/<dir>[/<dir> ...]"
dirPathsPlaceHolder = dirPathPlaceHolder + "..."
optionalDirPathPlaceHolder = repoPathPlaceHolder + "[/<dir> ...]"
secretPathPlaceHolder = optionalDirPathPlaceHolder + "/<secret>"
secretPathOptionalVersionPlaceHolder = secretPathPlaceHolder + "[:<version>]"
Expand Down

0 comments on commit 49f23fb

Please sign in to comment.