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
3 changes: 0 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
# A Self-Documenting Makefile: http://marmelab.com/blog/2016/02/29/auto-documented-makefile.html


GOLANGCI_VERSION=v1.43.0
COVERAGE=coverage.out


MCLI_SOURCE_FILES?=./cmd/mongocli
MCLI_BINARY_NAME=mongocli
MCLI_VERSION?=$(shell git tag --list 'mongocli/v*' --sort=committerdate | tail -1 | cut -d "v" -f 2 | xargs -I % sh -c 'echo %-next' )
Expand All @@ -13,7 +11,6 @@ MCLI_DESTINATION=./bin/$(MCLI_BINARY_NAME)
MCLI_INSTALL_PATH="${GOPATH}/bin/$(MCLI_BINARY_NAME)"
MCLI_E2E_BINARY?=../../bin/${MCLI_BINARY_NAME}


ATLAS_SOURCE_FILES?=./cmd/atlas
ATLAS_BINARY_NAME=atlas
ATLAS_VERSION?=$(shell git tag --list 'atlascli/v*' --sort=committerdate | tail -1 | cut -d "v" -f 2 | xargs -I % sh -c 'echo %-next' )
Expand Down
8 changes: 4 additions & 4 deletions internal/cli/atlas/config/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ func (opts *initOpts) SetUpAccess() {
}

func (opts *initOpts) Run(ctx context.Context) error {
fmt.Printf(`You are configuring a profile for %s.
_, _ = fmt.Fprintf(opts.OutWriter, `You are configuring a profile for %s.

All values are optional and you can use environment variables (MONGODB_ATLAS_*) instead.

Expand Down Expand Up @@ -89,11 +89,11 @@ Enter [?] on any option to get help.
return err
}

fmt.Printf("\nYour profile is now configured.\n")
_, _ = fmt.Fprintf(opts.OutWriter, "\nYour profile is now configured.\n")
if config.Name() != config.DefaultProfile {
fmt.Printf("To use this profile, you must set the flag [-%s %s] for every command.\n", flag.ProfileShort, config.Name())
_, _ = fmt.Fprintf(opts.OutWriter, "To use this profile, you must set the flag [-%s %s] for every command.\n", flag.ProfileShort, config.Name())
}
fmt.Printf("You can use [%s config set] to change these settings at a later time.\n", atlas)
_, _ = fmt.Fprintf(opts.OutWriter, "You can use [%s config set] to change these settings at a later time.\n", atlas)
return nil
}

Expand Down
36 changes: 2 additions & 34 deletions internal/cli/auth/login.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ package auth

import (
"context"
"errors"
"fmt"
"os"
"time"
Expand All @@ -27,7 +26,6 @@ import (
"github.com/mongodb/mongocli/internal/config"
"github.com/mongodb/mongocli/internal/flag"
"github.com/mongodb/mongocli/internal/oauth"
"github.com/mongodb/mongocli/internal/prompt"
"github.com/pkg/browser"
"github.com/spf13/cobra"
"go.mongodb.org/atlas/auth"
Expand Down Expand Up @@ -107,11 +105,11 @@ func (opts *loginOpts) Run(ctx context.Context) error {
}
_, _ = fmt.Fprint(opts.OutWriter, "Press Enter to continue your profile configuration")
_, _ = fmt.Scanln()
if err := opts.askOrg(); err != nil {
if err := opts.AskOrg(); err != nil {
return err
}
opts.SetUpOrg()
if err := opts.askProject(); err != nil {
if err := opts.AskProject(); err != nil {
return err
}
opts.SetUpProject()
Expand Down Expand Up @@ -168,36 +166,6 @@ Your code will expire after %.0f minutes.
return nil
}

func (opts *loginOpts) askOrg() error {
oMap, oSlice, err := opts.Orgs()
if err != nil || len(oSlice) == 0 {
return errors.New("no orgs")
}

p := prompt.NewOrgSelect(oSlice)
var orgID string
if err := survey.AskOne(p, &orgID); err != nil {
return err
}
opts.OrgID = oMap[orgID]
return nil
}

func (opts *loginOpts) askProject() error {
pMap, pSlice, err := opts.Projects()
if err != nil || len(pSlice) == 0 {
return errors.New("no projects")
}

p := prompt.NewProjectSelect(pSlice)
var projectID string
if err := survey.AskOne(p, &projectID); err != nil {
return err
}
opts.ProjectID = pMap[projectID]
return nil
}

func LoginBuilder() *cobra.Command {
opts := &loginOpts{}
cmd := &cobra.Command{
Expand Down
81 changes: 0 additions & 81 deletions internal/cli/config_opts.go

This file was deleted.

140 changes: 125 additions & 15 deletions internal/cli/default_setter_opts.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,14 @@ package cli

import (
"context"
"errors"
"fmt"
"io"
"os"

"github.com/AlecAivazis/survey/v2"
"github.com/mongodb/mongocli/internal/config"
"github.com/mongodb/mongocli/internal/mongosh"
"github.com/mongodb/mongocli/internal/prompt"
"github.com/mongodb/mongocli/internal/store"
"github.com/mongodb/mongocli/internal/validate"
atlas "go.mongodb.org/atlas/mongodbatlas"
Expand All @@ -34,6 +35,7 @@ import (
type ProjectOrgsLister interface {
Projects(*atlas.ListOptions) (interface{}, error)
Organizations(*atlas.OrganizationsListOptions) (*atlas.Organizations, error)
GetOrgProjects(string, *atlas.ListOptions) (interface{}, error)
}

type DefaultSetterOpts struct {
Expand Down Expand Up @@ -61,38 +63,67 @@ func (opts *DefaultSetterOpts) IsOpsManager() bool {
return opts.Service == config.OpsManagerService
}

const resultsLimit = 500

var (
errTooManyResults = errors.New("too many results")
errNoResults = errors.New("no results")
)

// Projects fetches projects and returns then as a slice of the format `nameIDFormat`,
// and a map such as `map[nameIDFormat]=ID`.
// This is necessary as we can only prompt using `nameIDFormat`
// and we want them to get the ID mapping to store on the config.
func (opts *DefaultSetterOpts) Projects() (pMap map[string]string, pSlice []string, err error) {
projects, err := opts.Store.Projects(nil)
// and we want them to get the ID mapping to store in the config.
func (opts *DefaultSetterOpts) projects() (pMap map[string]string, pSlice []string, err error) {
var projects interface{}
if opts.OrgID == "" {
projects, err = opts.Store.Projects(nil)
} else {
projects, err = opts.Store.GetOrgProjects(opts.OrgID, &atlas.ListOptions{ItemsPerPage: resultsLimit})
}
if err != nil {
_, _ = fmt.Fprintf(os.Stderr, "there was a problem fetching projects: %s\n", err)
return nil, nil, err
}
if opts.IsCloud() {
pMap, pSlice = atlasProjects(projects.(*atlas.Projects).Results)
} else {
pMap, pSlice = omProjects(projects.(*opsmngr.Projects).Results)
switch r := projects.(type) {
case *atlas.Projects:
if r.TotalCount == 0 {
return nil, nil, errNoResults
}
if r.TotalCount > resultsLimit {
return nil, nil, errTooManyResults
}
pMap, pSlice = atlasProjects(r.Results)
case *opsmngr.Projects:
if r.TotalCount == 0 {
return nil, nil, errNoResults
}
if r.TotalCount > resultsLimit {
return nil, nil, errTooManyResults
}
pMap, pSlice = omProjects(r.Results)
}

return pMap, pSlice, nil
}

// Orgs fetches organizations and returns then as a slice of the format `nameIDFormat`,
// and a map such as `map[nameIDFormat]=ID`.
// This is necessary as we can only prompt using `nameIDFormat`
// and we want them to get the ID mapping to store on the config.
func (opts *DefaultSetterOpts) Orgs() (oMap map[string]string, oSlice []string, err error) {
func (opts *DefaultSetterOpts) orgs() (oMap map[string]string, oSlice []string, err error) {
includeDeleted := false
orgs, err := opts.Store.Organizations(&atlas.OrganizationsListOptions{IncludeDeletedOrgs: &includeDeleted})
if orgs != nil && orgs.TotalCount > len(orgs.Results) {
orgs, err = opts.Store.Organizations(&atlas.OrganizationsListOptions{IncludeDeletedOrgs: &includeDeleted, ListOptions: atlas.ListOptions{ItemsPerPage: orgs.TotalCount}})
}
pagination := &atlas.OrganizationsListOptions{IncludeDeletedOrgs: &includeDeleted}
pagination.ItemsPerPage = resultsLimit
orgs, err := opts.Store.Organizations(pagination)
if err != nil {
_, _ = fmt.Fprintf(os.Stderr, "there was a problem fetching orgs: %s\n", err)
return nil, nil, err
}
if orgs.TotalCount == 0 {
return nil, nil, errNoResults
}
if orgs.TotalCount > resultsLimit {
return nil, nil, errTooManyResults
}
oMap = make(map[string]string, len(orgs.Results))
oSlice = make([]string, len(orgs.Results))
for i, o := range orgs.Results {
Expand All @@ -103,6 +134,85 @@ func (opts *DefaultSetterOpts) Orgs() (oMap map[string]string, oSlice []string,
return oMap, oSlice, nil
}

// AskProject will try to construct a select based on fetched projects.
// If it fails or there are no projects to show we fallback to ask for project by ID.
func (opts *DefaultSetterOpts) AskProject() error {
pMap, pSlice, err := opts.projects()
if err != nil {
var target *atlas.ErrorResponse
switch {
case errors.Is(err, errNoResults):
_, _ = fmt.Fprintln(opts.OutWriter, "You don't seem to have access to any project")
case errors.Is(err, errTooManyResults):
_, _ = fmt.Fprintf(opts.OutWriter, "You have access to more than %d projects\n", resultsLimit)
case errors.As(err, &target):
_, _ = fmt.Fprintf(opts.OutWriter, "There was an error fetching your projects: %s\n", target.Detail)
default:
_, _ = fmt.Fprintf(opts.OutWriter, "There was an error fetching your projects: %s\n", err)
}
p := &survey.Confirm{
Message: "Do you want to enter the Project ID manually?",
}
manually := true
if err2 := survey.AskOne(p, &manually); err2 != nil {
return err2
}
if manually {
p := prompt.NewProjectIDInput()
return survey.AskOne(p, &opts.ProjectID, survey.WithValidator(validate.OptionalObjectID))
}
_, _ = fmt.Fprint(opts.OutWriter, "Skipping default project setting\n")
return nil
}

p := prompt.NewProjectSelect(pSlice)
var projectID string
if err := survey.AskOne(p, &projectID); err != nil {
return err
}
opts.ProjectID = pMap[projectID]
return nil
}

// AskOrg will try to construct a select based on fetched organizations.
// If it fails or there are no organizations to show we fallback to ask for org by ID.
func (opts *DefaultSetterOpts) AskOrg() error {
oMap, oSlice, err := opts.orgs()
if err != nil {
var target *atlas.ErrorResponse
switch {
case errors.Is(err, errNoResults):
_, _ = fmt.Fprintln(opts.OutWriter, "You don't seem to have access to any organization")
case errors.Is(err, errTooManyResults):
_, _ = fmt.Fprintf(opts.OutWriter, "You have access to more than %d organizations\n", resultsLimit)
case errors.As(err, &target):
_, _ = fmt.Fprintf(opts.OutWriter, "There was an error fetching your organizations: %s\n", target.Detail)
default:
_, _ = fmt.Fprintf(opts.OutWriter, "There was an error fetching your organizations: %s\n", err)
}
p := &survey.Confirm{
Message: "Do you want to enter the Org ID manually?",
}
manually := true
if err2 := survey.AskOne(p, &manually); err2 != nil {
return err2
}
if manually {
p := prompt.NewOrgIDInput()
return survey.AskOne(p, &opts.OrgID, survey.WithValidator(validate.OptionalObjectID))
}
_, _ = fmt.Fprint(opts.OutWriter, "Skipping default organization setting\n")
return nil
}
p := prompt.NewOrgSelect(oSlice)
var orgID string
if err := survey.AskOne(p, &orgID); err != nil {
return err
}
opts.OrgID = oMap[orgID]
return nil
}

func (opts *DefaultSetterOpts) SetUpProject() {
if opts.ProjectID != "" {
config.SetProjectID(opts.ProjectID)
Expand Down
Loading