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

mongodb / mongodb-atlas-cli / 16782890860

06 Aug 2025 04:26PM UTC coverage: 57.885%. First build
16782890860

Pull #4096

github

cveticm
Replaces internal/api/transport with internal/transport
Pull Request #4096: CLOUDP-329793: Replace L1 transport

28 of 31 new or added lines in 3 files covered. (90.32%)

23800 of 41116 relevant lines covered (57.89%)

2.74 hits per line

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

69.83
/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/mongodb-atlas-cli/atlascli/internal/config"
24
        "github.com/mongodb/mongodb-atlas-cli/atlascli/internal/log"
25
        "github.com/mongodb/mongodb-atlas-cli/atlascli/internal/store"
26
        "github.com/mongodb/mongodb-atlas-cli/atlascli/internal/transport"
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) {
1✔
47
        if commandConverter == nil {
1✔
48
                return nil, errors.Join(ErrMissingDependency, errors.New("commandConverter is nil"))
×
49
        }
×
50

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

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

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

63
        return &Executor{
1✔
64
                commandConverter: commandConverter,
1✔
65
                httpClient:       httpClient,
1✔
66
                formatter:        formatter,
1✔
67
                logger:           logger,
1✔
68
        }, nil
1✔
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
        token, err := profile.Token()
1✔
75
        if err != nil {
1✔
NEW
76
                return nil, err
×
NEW
77
        }
×
78

79
        client, err := store.HTTPClient(
1✔
80
                profile.AuthType(),
1✔
81
                profile.PublicAPIKey(),
1✔
82
                profile.PrivateAPIKey(),
1✔
83
                token,
1✔
84
                profile.ClientID(),
1✔
85
                profile.ClientSecret(),
1✔
86
                transport.Default(),
1✔
87
        )
1✔
88
        if err != nil {
1✔
NEW
89
                return nil, err
×
90
        }
×
91

92
        configWrapper := NewAuthenticatedConfigWrapper(profile)
1✔
93
        commandConverter, err := NewDefaultCommandConverter(configWrapper)
1✔
94
        if err != nil {
1✔
95
                return nil, err
×
96
        }
×
97

98
        return NewExecutor(
1✔
99
                commandConverter,
1✔
100
                client,
1✔
101
                formatter,
1✔
102
                log.Default(),
1✔
103
        )
1✔
104
}
105

106
func (e *Executor) ensureInitialized() {
1✔
107
        if e.commandConverter == nil || e.httpClient == nil {
1✔
108
                // panic because this is developer error, not user error
×
109
                // should never happen
×
110
                panic("the executor was not properly initialized, use the NewExecutor method to initialize this struct")
×
111
        }
112
}
113

114
func (e *Executor) ExecuteCommand(ctx context.Context, commandRequest CommandRequest) (*CommandResponse, error) {
1✔
115
        e.ensureInitialized()
1✔
116

1✔
117
        // Set the content type
1✔
118
        if err := e.SetContentType(&commandRequest); err != nil {
1✔
119
                return nil, err
×
120
        }
×
121

122
        // Convert the request (api command definition + execution context) into a http request
123
        httpRequest, err := e.commandConverter.ConvertToHTTPRequest(commandRequest)
1✔
124
        if err != nil {
1✔
125
                return nil, errors.Join(ErrFailedToBuildHTTPRequest, err)
×
126
        }
×
127

128
        // Set the context, so we can cancel the request
129
        httpRequest = httpRequest.WithContext(ctx)
1✔
130
        e.logRequest(httpRequest)
1✔
131

1✔
132
        // Execute the request
1✔
133
        httpResponse, err := e.httpClient.Do(httpRequest)
1✔
134
        if err != nil {
1✔
135
                return nil, errors.Join(ErrFailedToConvertToHTTPRequest, err)
×
136
        }
×
137

138
        e.logResponse(httpResponse)
1✔
139

1✔
140
        //nolint: mnd // httpResponse.StatusCode >= StatusOK && httpResponse.StatusCode < StatusMultipleChoices makes this code harder to read
1✔
141
        isSuccess := httpResponse.StatusCode >= 200 && httpResponse.StatusCode < 300
1✔
142
        httpCode := httpResponse.StatusCode
1✔
143
        output := httpResponse.Body
1✔
144

1✔
145
        response := CommandResponse{
1✔
146
                IsSuccess: isSuccess,
1✔
147
                HTTPCode:  httpCode,
1✔
148
                Output:    output,
1✔
149
        }
1✔
150

1✔
151
        return &response, nil
1✔
152
}
153

154
func (e *Executor) SetContentType(commandRequest *CommandRequest) error {
1✔
155
        e.ensureInitialized()
1✔
156

1✔
157
        // Update the format if needed
1✔
158
        // For example if the requested format is a go template, change the request format to json
1✔
159
        contentType, err := e.formatter.ContentType(commandRequest.Format)
1✔
160
        if err != nil {
1✔
161
                return errors.Join(ErrFailedToHandleFormat, err)
×
162
        }
×
163
        commandRequest.ContentType = contentType
1✔
164

1✔
165
        return nil
1✔
166
}
167

168
// Log the request if the logger is set to debug
169
// Copied behavior and format used in the SDK: https://github.com/mongodb/atlas-sdk-go/blob/b3fee40e236a8ff2a1f1c160b6984a242136dbe6/admin/client.go#L322
170
func (e *Executor) logRequest(httpRequest *http.Request) {
1✔
171
        if !e.logger.IsDebugLevel() {
2✔
172
                return
1✔
173
        }
1✔
174

175
        dump, err := httputil.DumpRequestOut(httpRequest, true)
×
176
        if err != nil {
×
177
                return
×
178
        }
×
179

180
        _, _ = e.logger.Debugf("\n%s\n", string(dump))
×
181
}
182

183
// Log the response if the logger is set to debug
184
// Copied behavior and format used in the SDK: https://github.com/mongodb/atlas-sdk-go/blob/b3fee40e236a8ff2a1f1c160b6984a242136dbe6/admin/client.go#L335
185
func (e *Executor) logResponse(httpResponse *http.Response) {
1✔
186
        if !e.logger.IsDebugLevel() {
2✔
187
                return
1✔
188
        }
1✔
189

190
        dump, err := httputil.DumpResponse(httpResponse, true)
×
191
        if err != nil {
×
192
                return
×
193
        }
×
194

195
        _, _ = e.logger.Debugf("\n%s\n", string(dump))
×
196
}
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