@@ -16,13 +16,14 @@ package cli
16
16
17
17
import (
18
18
"context"
19
+ "errors"
19
20
"fmt"
20
21
"io"
21
- "os"
22
22
23
23
"github.com/AlecAivazis/survey/v2"
24
24
"github.com/mongodb/mongocli/internal/config"
25
25
"github.com/mongodb/mongocli/internal/mongosh"
26
+ "github.com/mongodb/mongocli/internal/prompt"
26
27
"github.com/mongodb/mongocli/internal/store"
27
28
"github.com/mongodb/mongocli/internal/validate"
28
29
atlas "go.mongodb.org/atlas/mongodbatlas"
@@ -34,6 +35,7 @@ import (
34
35
type ProjectOrgsLister interface {
35
36
Projects (* atlas.ListOptions ) (interface {}, error )
36
37
Organizations (* atlas.OrganizationsListOptions ) (* atlas.Organizations , error )
38
+ GetOrgProjects (string , * atlas.ListOptions ) (interface {}, error )
37
39
}
38
40
39
41
type DefaultSetterOpts struct {
@@ -61,38 +63,67 @@ func (opts *DefaultSetterOpts) IsOpsManager() bool {
61
63
return opts .Service == config .OpsManagerService
62
64
}
63
65
66
+ const resultsLimit = 500
67
+
68
+ var (
69
+ errTooManyResults = errors .New ("too many results" )
70
+ errNoResults = errors .New ("no results" )
71
+ )
72
+
64
73
// Projects fetches projects and returns then as a slice of the format `nameIDFormat`,
65
74
// and a map such as `map[nameIDFormat]=ID`.
66
75
// This is necessary as we can only prompt using `nameIDFormat`
67
- // and we want them to get the ID mapping to store on the config.
68
- func (opts * DefaultSetterOpts ) Projects () (pMap map [string ]string , pSlice []string , err error ) {
69
- projects , err := opts .Store .Projects (nil )
76
+ // and we want them to get the ID mapping to store in the config.
77
+ func (opts * DefaultSetterOpts ) projects () (pMap map [string ]string , pSlice []string , err error ) {
78
+ var projects interface {}
79
+ if opts .OrgID == "" {
80
+ projects , err = opts .Store .Projects (nil )
81
+ } else {
82
+ projects , err = opts .Store .GetOrgProjects (opts .OrgID , & atlas.ListOptions {ItemsPerPage : resultsLimit })
83
+ }
70
84
if err != nil {
71
- _ , _ = fmt .Fprintf (os .Stderr , "there was a problem fetching projects: %s\n " , err )
72
85
return nil , nil , err
73
86
}
74
- if opts .IsCloud () {
75
- pMap , pSlice = atlasProjects (projects .(* atlas.Projects ).Results )
76
- } else {
77
- pMap , pSlice = omProjects (projects .(* opsmngr.Projects ).Results )
87
+ switch r := projects .(type ) {
88
+ case * atlas.Projects :
89
+ if r .TotalCount == 0 {
90
+ return nil , nil , errNoResults
91
+ }
92
+ if r .TotalCount > resultsLimit {
93
+ return nil , nil , errTooManyResults
94
+ }
95
+ pMap , pSlice = atlasProjects (r .Results )
96
+ case * opsmngr.Projects :
97
+ if r .TotalCount == 0 {
98
+ return nil , nil , errNoResults
99
+ }
100
+ if r .TotalCount > resultsLimit {
101
+ return nil , nil , errTooManyResults
102
+ }
103
+ pMap , pSlice = omProjects (r .Results )
78
104
}
105
+
79
106
return pMap , pSlice , nil
80
107
}
81
108
82
109
// Orgs fetches organizations and returns then as a slice of the format `nameIDFormat`,
83
110
// and a map such as `map[nameIDFormat]=ID`.
84
111
// This is necessary as we can only prompt using `nameIDFormat`
85
112
// and we want them to get the ID mapping to store on the config.
86
- func (opts * DefaultSetterOpts ) Orgs () (oMap map [string ]string , oSlice []string , err error ) {
113
+ func (opts * DefaultSetterOpts ) orgs () (oMap map [string ]string , oSlice []string , err error ) {
87
114
includeDeleted := false
88
- orgs , err := opts .Store .Organizations (& atlas.OrganizationsListOptions {IncludeDeletedOrgs : & includeDeleted })
89
- if orgs != nil && orgs .TotalCount > len (orgs .Results ) {
90
- orgs , err = opts .Store .Organizations (& atlas.OrganizationsListOptions {IncludeDeletedOrgs : & includeDeleted , ListOptions : atlas.ListOptions {ItemsPerPage : orgs .TotalCount }})
91
- }
115
+ pagination := & atlas.OrganizationsListOptions {IncludeDeletedOrgs : & includeDeleted }
116
+ pagination .ItemsPerPage = resultsLimit
117
+ orgs , err := opts .Store .Organizations (pagination )
92
118
if err != nil {
93
- _ , _ = fmt .Fprintf (os .Stderr , "there was a problem fetching orgs: %s\n " , err )
94
119
return nil , nil , err
95
120
}
121
+ if orgs .TotalCount == 0 {
122
+ return nil , nil , errNoResults
123
+ }
124
+ if orgs .TotalCount > resultsLimit {
125
+ return nil , nil , errTooManyResults
126
+ }
96
127
oMap = make (map [string ]string , len (orgs .Results ))
97
128
oSlice = make ([]string , len (orgs .Results ))
98
129
for i , o := range orgs .Results {
@@ -103,6 +134,85 @@ func (opts *DefaultSetterOpts) Orgs() (oMap map[string]string, oSlice []string,
103
134
return oMap , oSlice , nil
104
135
}
105
136
137
+ // AskProject will try to construct a select based on fetched projects.
138
+ // If it fails or there are no projects to show we fallback to ask for project by ID.
139
+ func (opts * DefaultSetterOpts ) AskProject () error {
140
+ pMap , pSlice , err := opts .projects ()
141
+ if err != nil {
142
+ var target * atlas.ErrorResponse
143
+ switch {
144
+ case errors .Is (err , errNoResults ):
145
+ _ , _ = fmt .Fprintln (opts .OutWriter , "You don't seem to have access to any project" )
146
+ case errors .Is (err , errTooManyResults ):
147
+ _ , _ = fmt .Fprintf (opts .OutWriter , "You have access to more than %d projects\n " , resultsLimit )
148
+ case errors .As (err , & target ):
149
+ _ , _ = fmt .Fprintf (opts .OutWriter , "There was an error fetching your projects: %s\n " , target .Detail )
150
+ default :
151
+ _ , _ = fmt .Fprintf (opts .OutWriter , "There was an error fetching your projects: %s\n " , err )
152
+ }
153
+ p := & survey.Confirm {
154
+ Message : "Do you want to enter the Project ID manually?" ,
155
+ }
156
+ manually := true
157
+ if err2 := survey .AskOne (p , & manually ); err2 != nil {
158
+ return err2
159
+ }
160
+ if manually {
161
+ p := prompt .NewProjectIDInput ()
162
+ return survey .AskOne (p , & opts .ProjectID , survey .WithValidator (validate .OptionalObjectID ))
163
+ }
164
+ _ , _ = fmt .Fprint (opts .OutWriter , "Skipping default project setting\n " )
165
+ return nil
166
+ }
167
+
168
+ p := prompt .NewProjectSelect (pSlice )
169
+ var projectID string
170
+ if err := survey .AskOne (p , & projectID ); err != nil {
171
+ return err
172
+ }
173
+ opts .ProjectID = pMap [projectID ]
174
+ return nil
175
+ }
176
+
177
+ // AskOrg will try to construct a select based on fetched organizations.
178
+ // If it fails or there are no organizations to show we fallback to ask for org by ID.
179
+ func (opts * DefaultSetterOpts ) AskOrg () error {
180
+ oMap , oSlice , err := opts .orgs ()
181
+ if err != nil {
182
+ var target * atlas.ErrorResponse
183
+ switch {
184
+ case errors .Is (err , errNoResults ):
185
+ _ , _ = fmt .Fprintln (opts .OutWriter , "You don't seem to have access to any organization" )
186
+ case errors .Is (err , errTooManyResults ):
187
+ _ , _ = fmt .Fprintf (opts .OutWriter , "You have access to more than %d organizations\n " , resultsLimit )
188
+ case errors .As (err , & target ):
189
+ _ , _ = fmt .Fprintf (opts .OutWriter , "There was an error fetching your organizations: %s\n " , target .Detail )
190
+ default :
191
+ _ , _ = fmt .Fprintf (opts .OutWriter , "There was an error fetching your organizations: %s\n " , err )
192
+ }
193
+ p := & survey.Confirm {
194
+ Message : "Do you want to enter the Org ID manually?" ,
195
+ }
196
+ manually := true
197
+ if err2 := survey .AskOne (p , & manually ); err2 != nil {
198
+ return err2
199
+ }
200
+ if manually {
201
+ p := prompt .NewOrgIDInput ()
202
+ return survey .AskOne (p , & opts .OrgID , survey .WithValidator (validate .OptionalObjectID ))
203
+ }
204
+ _ , _ = fmt .Fprint (opts .OutWriter , "Skipping default organization setting\n " )
205
+ return nil
206
+ }
207
+ p := prompt .NewOrgSelect (oSlice )
208
+ var orgID string
209
+ if err := survey .AskOne (p , & orgID ); err != nil {
210
+ return err
211
+ }
212
+ opts .OrgID = oMap [orgID ]
213
+ return nil
214
+ }
215
+
106
216
func (opts * DefaultSetterOpts ) SetUpProject () {
107
217
if opts .ProjectID != "" {
108
218
config .SetProjectID (opts .ProjectID )
0 commit comments