diff --git a/internal/cli/config.go b/internal/cli/config.go index 700390f2a3..ee432ea8c2 100644 --- a/internal/cli/config.go +++ b/internal/cli/config.go @@ -93,6 +93,7 @@ func (opts *configOpts) Run() error { Help: "This is the ID of an existing project your API keys have access to, you can leave this blank and specify it on every command with --projectId", Default: config.ProjectID(), }, + Validate: validate.OptionalObjectID, }, } diff --git a/internal/validate/validate.go b/internal/validate/validate.go index c3c9a7939f..9e7e890f68 100644 --- a/internal/validate/validate.go +++ b/internal/validate/validate.go @@ -24,23 +24,49 @@ import ( "github.com/mongodb/mongocli/internal/config" ) -func URL(val interface{}) error { +// toString tires to cast an interface to string +func toString(val interface{}) (string, error) { var u string var ok bool if u, ok = val.(string); !ok { - return fmt.Errorf("'%v' is not a valid URL", val) + return "", fmt.Errorf("'%v' is not valid", val) + } + return u, nil +} + +// URL validates a value is a valid URL for the cli store +func URL(val interface{}) error { + s, err := toString(val) + if err != nil { + return err } - if !strings.HasSuffix(u, "/") { - return fmt.Errorf("'%s' must have a trailing slash", u) + if !strings.HasSuffix(s, "/") { + return fmt.Errorf("'%s' must have a trailing slash", s) } - _, err := url.ParseRequestURI(u) + _, err = url.ParseRequestURI(s) if err != nil { - return fmt.Errorf("'%s' is not a valid URL", u) + return fmt.Errorf("'%s' is not a valid URL", s) } return nil } +// OptionalObjectID validates a value is a valid ObjectID +func OptionalObjectID(val interface{}) error { + if val == nil { + return nil + } + s, err := toString(val) + if err != nil { + return err + } + if s == "" { + return nil + } + return ObjectID(s) +} + +// ObjectID validates a value is a valid ObjectID func ObjectID(s string) error { b, err := hex.DecodeString(s) if err != nil || len(b) != 12 { @@ -49,6 +75,7 @@ func ObjectID(s string) error { return nil } +// Credentials validates public and private API keys have been set func Credentials() error { if config.PrivateAPIKey() == "" || config.PublicAPIKey() == "" { return errors.New("missing credentials") diff --git a/internal/validate/validate_test.go b/internal/validate/validate_test.go index bea49402c0..06177e0b50 100644 --- a/internal/validate/validate_test.go +++ b/internal/validate/validate_test.go @@ -42,6 +42,39 @@ func TestURL(t *testing.T) { }) } +func TestOptionalObjectID(t *testing.T) { + t.Run("Empty value", func(t *testing.T) { + err := OptionalObjectID("") + if err != nil { + t.Fatalf("OptionalObjectID() unexpected error %v\n", err) + } + }) + t.Run("nil value", func(t *testing.T) { + err := OptionalObjectID(nil) + if err != nil { + t.Fatalf("OptionalObjectID() unexpected error %v\n", err) + } + }) + t.Run("Valid ObjectID", func(t *testing.T) { + err := OptionalObjectID("5e9f088b4797476aa0a5d56a") + if err != nil { + t.Fatalf("OptionalObjectID() unexpected error %v\n", err) + } + }) + t.Run("Short ObjectID", func(t *testing.T) { + err := OptionalObjectID("5e9f088b4797476aa0a5d56") + if err == nil { + t.Fatal("OptionalObjectID() expected an error\n") + } + }) + t.Run("Invalid ObjectID", func(t *testing.T) { + err := OptionalObjectID("5e9f088b4797476aa0a5d56z") + if err == nil { + t.Fatal("OptionalObjectID() expected an error\n") + } + }) +} + func TestObjectID(t *testing.T) { t.Run("Valid ObjectID", func(t *testing.T) { err := ObjectID("5e9f088b4797476aa0a5d56a")