• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mongodb / mongodb-atlas-cli / 17381128997

01 Sep 2025 02:55PM UTC coverage: 63.44% (-1.0%) from 64.472%
17381128997

push

github

web-flow
CLOUDP-304203: Add support to Service Accounts (#4130)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Melanija Cvetic <119604954+cveticm@users.noreply.github.com>
Co-authored-by: Jeroen Vervaeke <9132134+jeroenvervaeke@users.noreply.github.com>
Co-authored-by: Wesley Nabo <102980553+Waybo26@users.noreply.github.com>
Co-authored-by: Filipe Constantinov Menezes <filipe.menezes@mongodb.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: apix-bot[bot] <168195273+apix-bot[bot]@users.noreply.github.com>
Co-authored-by: Jeroen Vervaeke <jeroen.vervaeke@mongodb.com>

768 of 1597 new or added lines in 48 files covered. (48.09%)

218 existing lines in 11 files now uncovered.

26622 of 41964 relevant lines covered (63.44%)

0.79 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

74.29
/internal/api/executor.go
1
// Copyright 2024 MongoDB Inc
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
//     http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14

15
package api
16

17
import (
18
        "context"
19
        "errors"
20
        "net/http"
21
        "net/http/httputil"
22

23
        "github.com/mongodb/atlas-cli-core/config"
24
        "github.com/mongodb/atlas-cli-core/transport"
25
        "github.com/mongodb/mongodb-atlas-cli/atlascli/internal/log"
26
        "github.com/mongodb/mongodb-atlas-cli/atlascli/internal/store"
27
)
28

29
var (
30
        ErrFailedToAccessToken          = errors.New("failed to get access token")
31
        ErrFailedToConvertToHTTPRequest = errors.New("failed to convert to HTTP request")
32
        ErrFailedToExecuteHTTPRequest   = errors.New("failed to execute HTTP request")
33
        ErrFailedToGetBaseURL           = errors.New("failed to get base url")
34
        ErrFailedToHandleFormat         = errors.New("failed to handle format")
35
        ErrMissingDependency            = errors.New("missing executor dependency")
36
)
37

38
type Executor struct {
39
        commandConverter CommandConverter
40
        httpClient       Doer
41
        formatter        ResponseFormatter
42
        logger           Logger
43
}
44

45
// We're expecting a http client that's authenticated.
46
func NewExecutor(commandConverter CommandConverter, httpClient Doer, formatter ResponseFormatter, logger Logger) (*Executor, error) {
2✔
47
        if commandConverter == nil {
2✔
48
                return nil, errors.Join(ErrMissingDependency, errors.New("commandConverter is nil"))
×
49
        }
×
50

51
        if httpClient == nil {
2✔
52
                return nil, errors.Join(ErrMissingDependency, errors.New("httpClient is nil"))
×
53
        }
×
54

55
        if formatter == nil {
2✔
56
                return nil, errors.Join(ErrMissingDependency, errors.New("formatter is nil"))
×
57
        }
×
58

59
        if logger == nil {
2✔
60
                return nil, errors.Join(ErrMissingDependency, errors.New("logger is nil"))
×
61
        }
×
62

63
        return &Executor{
2✔
64
                commandConverter: commandConverter,
2✔
65
                httpClient:       httpClient,
2✔
66
                formatter:        formatter,
2✔
67
                logger:           logger,
2✔
68
        }, nil
2✔
69
}
70

71
// Executor wired up to use the default profile and static functions on config.
72
func NewDefaultExecutor(formatter ResponseFormatter) (*Executor, error) {
1✔
73
        profile := config.Default()
1✔
74

1✔
75
        client, err := store.HTTPClient(profile, transport.Default())
1✔
76
        if err != nil {
1✔
NEW
77
                return nil, err
×
UNCOV
78
        }
×
79

80
        configWrapper := NewAuthenticatedConfigWrapper(profile)
1✔
81
        commandConverter, err := NewDefaultCommandConverter(configWrapper)
1✔
82
        if err != nil {
1✔
83
                return nil, err
×
84
        }
×
85

86
        return NewExecutor(
1✔
87
                commandConverter,
1✔
88
                client,
1✔
89
                formatter,
1✔
90
                log.Default(),
1✔
91
        )
1✔
92
}
93

94
func (e *Executor) ensureInitialized() {
2✔
95
        if e.commandConverter == nil || e.httpClient == nil {
2✔
96
                // panic because this is developer error, not user error
×
97
                // should never happen
×
98
                panic("the executor was not properly initialized, use the NewExecutor method to initialize this struct")
×
99
        }
100
}
101

102
func (e *Executor) ExecuteCommand(ctx context.Context, commandRequest CommandRequest) (*CommandResponse, error) {
2✔
103
        e.ensureInitialized()
2✔
104

2✔
105
        // Set the content type
2✔
106
        if err := e.SetContentType(&commandRequest); err != nil {
2✔
107
                return nil, err
×
108
        }
×
109

110
        // Convert the request (api command definition + execution context) into a http request
111
        httpRequest, err := e.commandConverter.ConvertToHTTPRequest(commandRequest)
2✔
112
        if err != nil {
2✔
113
                return nil, errors.Join(ErrFailedToBuildHTTPRequest, err)
×
114
        }
×
115

116
        // Set the context, so we can cancel the request
117
        httpRequest = httpRequest.WithContext(ctx)
2✔
118
        e.logRequest(httpRequest)
2✔
119

2✔
120
        // Execute the request
2✔
121
        httpResponse, err := e.httpClient.Do(httpRequest)
2✔
122
        if err != nil {
2✔
123
                return nil, errors.Join(ErrFailedToConvertToHTTPRequest, err)
×
124
        }
×
125

126
        e.logResponse(httpResponse)
2✔
127

2✔
128
        //nolint: mnd // httpResponse.StatusCode >= StatusOK && httpResponse.StatusCode < StatusMultipleChoices makes this code harder to read
2✔
129
        isSuccess := httpResponse.StatusCode >= 200 && httpResponse.StatusCode < 300
2✔
130
        httpCode := httpResponse.StatusCode
2✔
131
        output := httpResponse.Body
2✔
132

2✔
133
        response := CommandResponse{
2✔
134
                IsSuccess: isSuccess,
2✔
135
                HTTPCode:  httpCode,
2✔
136
                Output:    output,
2✔
137
        }
2✔
138

2✔
139
        return &response, nil
2✔
140
}
141

142
func (e *Executor) SetContentType(commandRequest *CommandRequest) error {
2✔
143
        e.ensureInitialized()
2✔
144

2✔
145
        // Update the format if needed
2✔
146
        // For example if the requested format is a go template, change the request format to json
2✔
147
        contentType, err := e.formatter.ContentType(commandRequest.Format)
2✔
148
        if err != nil {
2✔
149
                return errors.Join(ErrFailedToHandleFormat, err)
×
150
        }
×
151
        commandRequest.ContentType = contentType
2✔
152

2✔
153
        return nil
2✔
154
}
155

156
// Log the request if the logger is set to debug
157
// Copied behavior and format used in the SDK: https://github.com/mongodb/atlas-sdk-go/blob/b3fee40e236a8ff2a1f1c160b6984a242136dbe6/admin/client.go#L322
158
func (e *Executor) logRequest(httpRequest *http.Request) {
2✔
159
        if !e.logger.IsDebugLevel() {
4✔
160
                return
2✔
161
        }
2✔
162

163
        dump, err := httputil.DumpRequestOut(httpRequest, true)
1✔
164
        if err != nil {
1✔
165
                return
×
166
        }
×
167

168
        _, _ = e.logger.Debugf("\n%s\n", string(dump))
1✔
169
}
170

171
// Log the response if the logger is set to debug
172
// Copied behavior and format used in the SDK: https://github.com/mongodb/atlas-sdk-go/blob/b3fee40e236a8ff2a1f1c160b6984a242136dbe6/admin/client.go#L335
173
func (e *Executor) logResponse(httpResponse *http.Response) {
2✔
174
        if !e.logger.IsDebugLevel() {
4✔
175
                return
2✔
176
        }
2✔
177

178
        dump, err := httputil.DumpResponse(httpResponse, true)
1✔
179
        if err != nil {
1✔
180
                return
×
181
        }
×
182

183
        _, _ = e.logger.Debugf("\n%s\n", string(dump))
1✔
184
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc