@@ -22,14 +22,29 @@ const (
2222 defaultBaseURL = "https://cloud.mongodb.com/api/atlas/v1.0/"
2323 userAgent = "go-mongodbatlas" + libraryVersion
2424 mediaType = "application/json"
25+ gzipMediaType = "application/gzip"
2526)
2627
27- type RequestDoer interface {
28- NewRequest (context.Context , string , string , interface {}) (* http.Request , error )
28+ type Doer interface {
2929 Do (context.Context , * http.Request , interface {}) (* Response , error )
30+ }
31+
32+ type Completer interface {
3033 OnRequestCompleted (RequestCompletionCallback )
3134}
3235
36+ type RequestDoer interface {
37+ Doer
38+ Completer
39+ NewRequest (context.Context , string , string , interface {}) (* http.Request , error )
40+ }
41+
42+ type GZipRequestDoer interface {
43+ Doer
44+ Completer
45+ NewGZipRequest (context.Context , string , string ) (* http.Request , error )
46+ }
47+
3348// Client manages communication with MongoDBAtlas v1.0 API
3449type Client struct {
3550 client * http.Client
@@ -71,6 +86,7 @@ type Client struct {
7186 ProcessDiskMeasurements ProcessDiskMeasurementsService
7287 ProcessDatabases ProcessDatabasesService
7388 Indexes IndexesService
89+ Logs LogsService
7490
7591 onRequestCompleted RequestCompletionCallback
7692}
@@ -195,6 +211,7 @@ func NewClient(httpClient *http.Client) *Client {
195211 c .ProcessDiskMeasurements = & ProcessDiskMeasurementsServiceOp {Client : c }
196212 c .ProcessDatabases = & ProcessDatabasesServiceOp {Client : c }
197213 c .Indexes = & IndexesServiceOp {Client : c }
214+ c .Logs = & LogsServiceOp {Client : c }
198215
199216 return c
200217}
@@ -239,17 +256,13 @@ func SetUserAgent(ua string) ClientOpt {
239256// BaseURL of the Client. Relative URLS should always be specified without a preceding slash. If specified, the
240257// value pointed to by body is JSON encoded and included in as the request body.
241258func (c * Client ) NewRequest (ctx context.Context , method , urlStr string , body interface {}) (* http.Request , error ) {
242- rel , err := url .Parse (urlStr )
259+ u , err := c . BaseURL .Parse (urlStr )
243260 if err != nil {
244261 return nil , err
245262 }
246-
247- u := c .BaseURL .ResolveReference (rel )
248-
249- buf := new (bytes.Buffer )
263+ var buf io.Reader
250264 if body != nil {
251- err = json .NewEncoder (buf ).Encode (body )
252- if err != nil {
265+ if buf , err = c .newEncodedBody (body ); err != nil {
253266 return nil , err
254267 }
255268 }
@@ -259,9 +272,44 @@ func (c *Client) NewRequest(ctx context.Context, method, urlStr string, body int
259272 return nil , err
260273 }
261274
262- req .Header .Add ("Content-Type" , mediaType )
275+ if body != nil {
276+ req .Header .Set ("Content-Type" , mediaType )
277+ }
263278 req .Header .Add ("Accept" , mediaType )
264- req .Header .Add ("User-Agent" , c .UserAgent )
279+ if c .UserAgent != "" {
280+ req .Header .Set ("User-Agent" , c .UserAgent )
281+ }
282+ return req , nil
283+ }
284+
285+ // newEncodedBody returns an ReadWriter object containing the body of the http request
286+ func (c * Client ) newEncodedBody (body interface {}) (io.Reader , error ) {
287+ buf := & bytes.Buffer {}
288+ enc := json .NewEncoder (buf )
289+ enc .SetEscapeHTML (false )
290+ err := enc .Encode (body )
291+ return buf , err
292+ }
293+
294+ // NewGZipRequest creates an API request that accepts gzip. A relative URL can be provided in urlStr, which will be resolved to the
295+ // BaseURL of the Client. Relative URLS should always be specified without a preceding slash.
296+ func (c * Client ) NewGZipRequest (ctx context.Context , method , urlStr string ) (* http.Request , error ) {
297+ rel , err := url .Parse (urlStr )
298+ if err != nil {
299+ return nil , err
300+ }
301+
302+ u := c .BaseURL .ResolveReference (rel )
303+
304+ req , err := http .NewRequest (method , u .String (), nil )
305+ if err != nil {
306+ return nil , err
307+ }
308+
309+ req .Header .Add ("Accept" , gzipMediaType )
310+ if c .UserAgent != "" {
311+ req .Header .Set ("User-Agent" , c .UserAgent )
312+ }
265313 return req , nil
266314}
267315
@@ -276,6 +324,14 @@ func (c *Client) OnRequestCompleted(rc RequestCompletionCallback) {
276324func (c * Client ) Do (ctx context.Context , req * http.Request , v interface {}) (* Response , error ) {
277325 resp , err := DoRequestWithClient (ctx , c .client , req )
278326 if err != nil {
327+ // If we got an error, and the context has been canceled,
328+ // the context's error is probably more useful.
329+ select {
330+ case <- ctx .Done ():
331+ return nil , ctx .Err ()
332+ default :
333+ }
334+
279335 return nil , err
280336 }
281337 if c .onRequestCompleted != nil {
@@ -288,7 +344,7 @@ func (c *Client) Do(ctx context.Context, req *http.Request, v interface{}) (*Res
288344 }
289345 }()
290346
291- response := newResponse ( resp )
347+ response := & Response { Response : resp }
292348
293349 err = CheckResponse (resp )
294350 if err != nil {
@@ -302,9 +358,12 @@ func (c *Client) Do(ctx context.Context, req *http.Request, v interface{}) (*Res
302358 return nil , err
303359 }
304360 } else {
305- err = json .NewDecoder (resp .Body ).Decode (v )
306- if err != nil {
307- return nil , err
361+ decErr := json .NewDecoder (resp .Body ).Decode (v )
362+ if decErr == io .EOF {
363+ decErr = nil // ignore EOF errors caused by empty response body
364+ }
365+ if decErr != nil {
366+ err = decErr
308367 }
309368 }
310369 }
@@ -338,13 +397,6 @@ func CheckResponse(r *http.Response) error {
338397 return errorResponse
339398}
340399
341- // newResponse creates a new Response for the provided http.Response
342- func newResponse (r * http.Response ) * Response {
343- response := Response {Response : r }
344-
345- return & response
346- }
347-
348400// DoRequestWithClient submits an HTTP request using the specified client.
349401func DoRequestWithClient (
350402 ctx context.Context ,
0 commit comments