Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 20 additions & 1 deletion cmd/cloudx/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,21 @@ func RegisterProjectFlag(f *flag.FlagSet) {
f.String(projectFlag, "", "The project to use, either project ID or a (partial) slug.")
}

// ProjectOrDefault returns the slug or ID the user set with the `--project` flag, or the default project, or prints a warning and returns an error
// if none was set.
func ProjectOrDefault(cmd *cobra.Command, h *CommandHelper) (string, error) {
if flag := flagx.MustGetString(cmd, projectFlag); flag == "" {
if id := h.GetDefaultProjectID(); id != "" {
return id, nil
} else {
_, _ = fmt.Fprintf(os.Stderr, "No project selected! Please use the flag --%s to specify one.\n", projectFlag)
return "", cmdx.FailSilently(cmd)
}
} else {
return flag, nil
}
}

func Client(cmd *cobra.Command) (*retryablehttp.Client, *AuthContext, *cloud.Project, error) {
sc, err := NewCommandHelper(cmd)
if err != nil {
Expand All @@ -42,7 +57,11 @@ func Client(cmd *cobra.Command) (*retryablehttp.Client, *AuthContext, *cloud.Pro
return nil, nil, nil, err
}

projectOrSlug := flagx.MustGetString(cmd, projectFlag)
projectOrSlug, err := ProjectOrDefault(cmd, sc)
if err != nil {
return nil, nil, nil, cmdx.FailSilently(cmd)
}

p, err := sc.GetProject(projectOrSlug)
if err != nil {
return nil, nil, nil, err
Expand Down
26 changes: 18 additions & 8 deletions cmd/cloudx/client/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,19 @@ func NewCommandHelper(cmd *cobra.Command) (*CommandHelper, error) {
}, nil
}

func (h *CommandHelper) GetDefaultProjectID() string {
conf, err := h.readConfig()
if err != nil {
return ""
}

if conf.SelectedProject != uuid.Nil {
return conf.SelectedProject.String()
}

return ""
}

func (h *CommandHelper) SetDefaultProject(id string) error {
conf, err := h.readConfig()
if err != nil {
Expand Down Expand Up @@ -455,11 +468,6 @@ func (h *CommandHelper) Authenticate() (*AuthContext, error) {
return nil, err
}

var retry bool
if retry {
_, _ = fmt.Fprintln(h.VerboseErrWriter, "Unable to Authenticate you, please try again.")
}

if signIn {
ac, err = h.signin(c, "")
if err != nil {
Expand Down Expand Up @@ -555,7 +563,7 @@ func (h *CommandHelper) GetProject(projectOrSlug string) (*cloud.Project, error)
return project, nil
}

func (h *CommandHelper) CreateProject(name string) (*cloud.Project, error) {
func (h *CommandHelper) CreateProject(name string, setDefault bool) (*cloud.Project, error) {
ac, err := h.EnsureContext()
if err != nil {
return nil, err
Expand All @@ -571,8 +579,8 @@ func (h *CommandHelper) CreateProject(name string) (*cloud.Project, error) {
return nil, handleError("unable to list projects", res, err)
}

if err := h.SetDefaultProject(project.Id); err != nil {
return nil, err
if def := h.GetDefaultProjectID(); setDefault || def == "" {
_ = h.SetDefaultProject(project.Id)
}

return project, nil
Expand Down Expand Up @@ -662,6 +670,7 @@ func (h *CommandHelper) PatchProject(id string, raw []json.RawMessage, add, repl
if err != nil {
return nil, err
}

return res, nil
}

Expand Down Expand Up @@ -732,6 +741,7 @@ func (h *CommandHelper) UpdateProject(id string, name string, configs []json.Raw
if err != nil {
return nil, err
}

return res, nil
}

Expand Down
199 changes: 199 additions & 0 deletions cmd/cloudx/client/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,205 @@ func TestCommandHelper(t *testing.T) {
return &notYetLoggedIn
}

t.Run("func=SetDefaultProject", func(t *testing.T) {
Comment thread
CaptainStandby marked this conversation as resolved.
t.Parallel()
configDir := testhelpers.NewConfigDir(t)
testhelpers.RegisterAccount(t, configDir)
otherId := testhelpers.CreateProject(t, configDir)
defaultId := testhelpers.CreateProject(t, configDir)
testhelpers.SetDefaultProject(t, configDir, defaultId)

cmdBase := client.CommandHelper{
ConfigLocation: configDir,
}

t.Run("can change the selected project", func(t *testing.T) {
cmd := cmdBase
current := cmd.GetDefaultProjectID()
assert.Equal(t, current, defaultId)

err := cmd.SetDefaultProject(otherId)
assert.NoError(t, err)

selected := cmd.GetDefaultProjectID()
assert.Equal(t, selected, otherId)
})
})

t.Run("func=ListProjects", func(t *testing.T) {
t.Parallel()
configDir := testhelpers.NewConfigDir(t)
testhelpers.RegisterAccount(t, configDir)

cmdBase := client.CommandHelper{
ConfigLocation: configDir,
}

t.Run("With no projects returns empty list", func(t *testing.T) {
cmd := cmdBase

projects, err := cmd.ListProjects()

require.NoError(t, err)
require.Empty(t, projects)
})

t.Run("With some projects returns list of projects", func(t *testing.T) {
cmd := cmdBase
project_name1 := "new_project_name1"
project_name2 := "new_project_name2"

project1, err := cmd.CreateProject(project_name1, false)
require.NoError(t, err)
project2, err := cmd.CreateProject(project_name2, false)
require.NoError(t, err)

projects, err := cmd.ListProjects()

require.NoError(t, err)
assert.Len(t, projects, 2)
assert.ElementsMatch(t, []string{projects[0].Id, projects[1].Id}, []string{project1.Id, project2.Id})
})
})

t.Run("func=CreateProject", func(t *testing.T) {
t.Parallel()
configDir := testhelpers.NewConfigDir(t)
testhelpers.RegisterAccount(t, configDir)

cmdBase := client.CommandHelper{
ConfigLocation: configDir,
}

t.Run("creates project and sets default project", func(t *testing.T) {
cmd := cmdBase
project_name := "new_project_name"

project, err := cmd.CreateProject(project_name, true)
require.NoError(t, err)
assert.Equal(t, project.Name, project_name)

defaultId := cmd.GetDefaultProjectID()
assert.Equal(t, project.Id, defaultId)
})

t.Run("creates two projects with different names", func(t *testing.T) {
cmd := cmdBase
project_name1 := "new_project_name1"
project_name2 := "new_project_name2"

project1, err := cmd.CreateProject(project_name1, true)
require.NoError(t, err)

project2, err := cmd.CreateProject(project_name2, false)
require.NoError(t, err)

assert.NotEqual(t, project1.Id, project2.Id)
assert.NotEqual(t, project1.Name, project2.Name)
assert.NotEqual(t, project1.Slug, project2.Slug)

defaultId := cmd.GetDefaultProjectID()
assert.Equal(t, project1.Id, defaultId)
})
})

t.Run("func=Authenticate", func(t *testing.T) {
t.Parallel()
cmdBase := client.CommandHelper{
ConfigLocation: testhelpers.NewConfigDir(t),
NoConfirm: true,
IsQuiet: false,
VerboseWriter: io.Discard,
VerboseErrWriter: io.Discard,
}

t.Run("create new account", func(t *testing.T) {
cmd := cmdBase

name := testhelpers.FakeName()
email := testhelpers.FakeEmail()
var r bytes.Buffer
_, _ = r.WriteString("n\n") // Do you want to sign in to an existing Ory Network account? [y/n]: n
_, _ = r.WriteString(email + "\n") // Email: FakeEmail()
_, _ = r.WriteString(name + "\n") // Name: FakeName()
_, _ = r.WriteString("n\n") // Subscribe to the Ory Security Newsletter to get platform and security updates? [y/n]: n
_, _ = r.WriteString("y\n") // I accept the Terms of Service [y/n]: y
Comment thread
hperl marked this conversation as resolved.
cmd.Stdin = bufio.NewReader(&r)

password := testhelpers.FakePassword()
cmd.PwReader = func() ([]byte, error) { return []byte(password), nil }

authCtx, err := cmd.Authenticate()

require.NoError(t, err)
require.NotNil(t, authCtx)
require.Equal(t, authCtx.IdentityTraits.Email, email)
})

t.Run("log into existing account", func(t *testing.T) {
cmd := cmdBase

var r bytes.Buffer
_, _ = r.WriteString("y\n") // Do you want to sign in to an existing Ory Network account? [y/n]: y
_, _ = r.WriteString(email + "\n") // Email: FakeEmail()
cmd.Stdin = bufio.NewReader(&r)

cmd.PwReader = func() ([]byte, error) { return []byte(password), nil }

auth_ctx, err := cmd.Authenticate()

require.NoError(t, err)
require.NotNil(t, auth_ctx)
require.Equal(t, auth_ctx.IdentityTraits.Email, email)
})

t.Run("retry login after wrong password", func(t *testing.T) {
cmd := cmdBase

var r bytes.Buffer
_, _ = r.WriteString("y\n") // Do you want to sign in to an existing Ory Network account? [y/n]: y
_, _ = r.WriteString(email + "\n") // Email: FakeEmail()
_, _ = r.WriteString(email + "\n") // Email: FakeEmail() [RETRY]
cmd.Stdin = bufio.NewReader(&r)

var retry = false
cmd.PwReader = func() ([]byte, error) {
if retry {
return []byte(password), nil
}
retry = true
return []byte("wrong"), nil
}

auth_ctx, err := cmd.Authenticate()

require.NoError(t, err)
require.NotNil(t, auth_ctx)
require.Equal(t, auth_ctx.IdentityTraits.Email, email)
})

t.Run("switch logged in account", func(t *testing.T) {
cmd := *loggedIn

cmd.NoConfirm = false
cmd.IsQuiet = false

var r bytes.Buffer
_, _ = r.WriteString("y\n") // You are signed in as \"%s\" already. Do you wish to authenticate with another account?: y
_, _ = r.WriteString("y\n") // Do you want to sign in to an existing Ory Network account? [y/n]: y
_, _ = r.WriteString(email + "\n") // Email: FakeEmail()
cmd.Stdin = bufio.NewReader(&r)

cmd.PwReader = func() ([]byte, error) { return []byte(password), nil }

auth_ctx, err := cmd.Authenticate()

require.NoError(t, err)
require.NotNil(t, auth_ctx)
require.Equal(t, auth_ctx.IdentityTraits.Email, email)
})
})

t.Run("func=CreateAPIKey and DeleteApiKey", func(t *testing.T) {
t.Run("is able to get project", func(t *testing.T) {
name := "a test key"
Expand Down
2 changes: 1 addition & 1 deletion cmd/cloudx/identity/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,6 @@ var (
)

func TestMain(m *testing.M) {
defaultConfig, defaultEmail, defaultPassword, defaultProject, defaultCmd = testhelpers.CreateDefaultAssets()
defaultConfig, defaultEmail, defaultPassword, _, defaultProject, defaultCmd = testhelpers.CreateDefaultAssets()
testhelpers.RunAgainstStaging(m)
}
2 changes: 1 addition & 1 deletion cmd/cloudx/oauth2/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,6 @@ var (
)

func TestMain(m *testing.M) {
defaultConfig, defaultEmail, defaultPassword, defaultProject, defaultCmd = testhelpers.CreateDefaultAssets()
defaultConfig, defaultEmail, defaultPassword, _, defaultProject, defaultCmd = testhelpers.CreateDefaultAssets()
testhelpers.RunAgainstStaging(m)
}
6 changes: 5 additions & 1 deletion cmd/cloudx/project/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import (
"github.com/ory/x/flagx"
)

const useProjectFlag = "use-project"

func NewCreateProjectCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "project",
Expand All @@ -39,7 +41,8 @@ func NewCreateProjectCmd() *cobra.Command {
}
}

p, err := h.CreateProject(name)
use := flagx.MustGetBool(cmd, useProjectFlag)
p, err := h.CreateProject(name, use)
if err != nil {
return cmdx.PrintOpenAPIError(cmd, err)
}
Expand All @@ -51,6 +54,7 @@ func NewCreateProjectCmd() *cobra.Command {
}

cmd.Flags().StringP("name", "n", "", "The name of the project, required when quiet mode is used")
cmd.Flags().Bool(useProjectFlag, false, "Set the created project as the default.")
cmdx.RegisterFormatFlags(cmd.Flags())
return cmd
}
28 changes: 24 additions & 4 deletions cmd/cloudx/project/create_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,38 @@ import (
)

func TestCreateProject(t *testing.T) {
parseOutput := func(stdout string) (id string, slug string, name string) {
id = gjson.Get(stdout, "id").String()
slug = gjson.Get(stdout, "slug").String()
name = gjson.Get(stdout, "name").String()
return
}
assertResult := func(t *testing.T, configDir string, stdout string, expectedName string) {
ac := testhelpers.ReadConfig(t, configDir)
assert.Equal(t, gjson.Get(stdout, "id").String(), ac.SelectedProject.String(), stdout)
assert.NotEmpty(t, gjson.Get(stdout, "slug").String(), stdout)
assert.Equal(t, expectedName, gjson.Get(stdout, "name").String(), stdout)
_, slug, name := parseOutput(stdout)
assert.NotEmpty(t, slug, stdout)
assert.Equal(t, expectedName, name, stdout)
}

t.Run("is able to create a project", func(t *testing.T) {
testhelpers.SetDefaultProject(t, defaultConfig, defaultProject)

name := testhelpers.TestProjectName()
stdout, _, err := defaultCmd.Exec(nil, "create", "project", "--name", name, "--format", "json")
require.NoError(t, err)
assertResult(t, defaultConfig, stdout, name)

assert.Equal(t, defaultProject, testhelpers.GetDefaultProject(t, defaultConfig))
})

t.Run("is able to create a project and use the project as default", func(t *testing.T) {
name := testhelpers.TestProjectName()

stdout, _, err := defaultCmd.Exec(nil, "create", "project", "--name", name, "--use-project", "--format", "json")
require.NoError(t, err)
assertResult(t, defaultConfig, stdout, name)

id, _, _ := parseOutput(stdout)
assert.Equal(t, id, testhelpers.GetDefaultProject(t, defaultConfig))
})

t.Run("is able to create a project and use name from stdin", func(t *testing.T) {
Expand Down
Loading