Skip to content

Commit 84f7346

Browse files
CLOUDP-279931: Add ability to control user agent for token endpoints (#457)
1 parent df94619 commit 84f7346

File tree

6 files changed

+81
-23
lines changed

6 files changed

+81
-23
lines changed

admin/atlas_client.go

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,34 +2,27 @@ package admin // import "go.mongodb.org/atlas-sdk/v20240805005/admin"
22

33
import (
44
"errors"
5-
"fmt"
65
"net/http"
7-
"runtime"
86
"strings"
97

108
"github.com/mongodb-forks/digest"
119
"go.mongodb.org/atlas-sdk/v20240805005/internal/core"
1210
)
1311

1412
const (
15-
// DefaultCloudURL is default base URL for the services.
16-
DefaultCloudURL = "https://cloud.mongodb.com"
1713
// Version the version of the current API client inherited from.
1814
Version = core.Version
19-
// ClientName of the v2 API client.
20-
ClientName = "go-atlas-sdk-admin"
2115
)
2216

2317
// NewClient returns a new API Client.
2418
func NewClient(modifiers ...ClientModifier) (*APIClient, error) {
25-
userAgent := fmt.Sprintf("%s/%s (%s;%s)", ClientName, Version, runtime.GOOS, runtime.GOARCH)
2619
defaultConfig := &Configuration{
2720
HTTPClient: http.DefaultClient,
2821
Servers: ServerConfigurations{ServerConfiguration{
29-
URL: DefaultCloudURL,
22+
URL: core.DefaultCloudURL,
3023
},
3124
},
32-
UserAgent: userAgent,
25+
UserAgent: core.DefaultUserAgent,
3326
}
3427
for _, modifierFunction := range modifiers {
3528
err := modifierFunction(defaultConfig)
@@ -87,7 +80,7 @@ func UseDebug(debug bool) ClientModifier {
8780
func UseBaseURL(baseURL string) ClientModifier {
8881
return func(c *Configuration) error {
8982
if baseURL == "" {
90-
baseURL = DefaultCloudURL
83+
baseURL = core.DefaultCloudURL
9184
}
9285
urlWithoutSuffix := strings.TrimSuffix(baseURL, "/")
9386
c.Servers = ServerConfigurations{ServerConfiguration{

auth/credentials/api.go

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package credentials
22

33
import (
44
"context"
5+
"go.mongodb.org/atlas-sdk/v20240805005/internal/core"
56
"net/http"
67
)
78

@@ -11,13 +12,15 @@ import (
1112
const tokenAPIPath = "/api/oauth/token"
1213

1314
// serverURL for atlas API
14-
const serverURL = "https://cloud.mongodb.com" + tokenAPIPath
15+
const serverURL = core.DefaultCloudURL + tokenAPIPath
1516

1617
// AtlasTokenSourceOptions provides set of input arguments
1718
// for creation of credentials.TokenSource interface
1819
type AtlasTokenSourceOptions struct {
1920
ClientID string
2021
ClientSecret string
22+
// Custom user agent. core.DefaultUserAgent being default
23+
UserAgent string
2124
// Custom Token source. InMemoryTokenCache being default
2225
TokenCache LocalTokenCache
2326

@@ -28,14 +31,19 @@ type AtlasTokenSourceOptions struct {
2831
}
2932

3033
// NewTokenSourceWithOptions initializes an OAuthTokenSource with advanced credentials.AtlasTokenSourceOptions
31-
// Use this method to initialize custom OAuth Token Cache (filesystem).
3234
func NewTokenSourceWithOptions(opts AtlasTokenSourceOptions) TokenSource {
3335
var tokenURL string
3436
if opts.BaseURL != nil {
3537
tokenURL = *opts.BaseURL + tokenAPIPath
3638
} else {
3739
tokenURL = serverURL
3840
}
41+
var userAgent string
42+
if opts.UserAgent != "" {
43+
userAgent = opts.UserAgent
44+
} else {
45+
userAgent = core.DefaultUserAgent
46+
}
3947
var ctx context.Context
4048
if opts.Context == nil {
4149
ctx = context.Background()
@@ -46,6 +54,7 @@ func NewTokenSourceWithOptions(opts AtlasTokenSourceOptions) TokenSource {
4654
return &OAuthTokenSource{
4755
clientID: opts.ClientID,
4856
clientSecret: opts.ClientSecret,
57+
userAgent: userAgent,
4958
tokenURL: tokenURL,
5059
tokenCache: opts.TokenCache,
5160
ctx: ctx,
@@ -58,6 +67,7 @@ func NewTokenSource(clientID, clientSecret string) TokenSource {
5867
return NewTokenSourceWithOptions(AtlasTokenSourceOptions{
5968
ClientID: clientID,
6069
ClientSecret: clientSecret,
70+
UserAgent: core.DefaultUserAgent,
6171
TokenCache: &InMemoryTokenCache{},
6272
})
6373
}

auth/credentials/oauth.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ type TokenSource interface {
2929
type OAuthTokenSource struct {
3030
clientID string
3131
clientSecret string
32+
userAgent string
3233
tokenURL string
3334
token *Token
3435
tokenCache LocalTokenCache
@@ -81,6 +82,7 @@ func (c *OAuthTokenSource) fetchToken() (*Token, error) {
8182
}
8283
req.SetBasicAuth(c.clientID, c.clientSecret)
8384
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
85+
req.Header.Set("User-Agent", c.userAgent)
8486

8587
client := &http.Client{}
8688
resp, err := client.Do(req)

auth/credentials/oauth_test.go

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"encoding/base64"
66
"encoding/json"
77
"fmt"
8+
"go.mongodb.org/atlas-sdk/v20240805005/internal/core"
89
"log"
910
"net/http"
1011
"net/http/httptest"
@@ -146,7 +147,7 @@ func TestOAuthClient_FetchToken_Failure(t *testing.T) {
146147
assert.Contains(t, err.Error(), "failed to obtain Token")
147148
}
148149

149-
// // Test CustomTransport to ensure Token is injected into requests
150+
// Test CustomTransport to ensure Token is injected into requests
150151
func TestCustomTransport_RoundTrip(t *testing.T) {
151152
// Mock the OAuth TokenCache
152153
expiration := time.Now().Add(1 * time.Hour)
@@ -186,3 +187,48 @@ func TestCustomTransport_RoundTrip(t *testing.T) {
186187
assert.NoError(t, err)
187188
assert.Equal(t, http.StatusOK, resp.StatusCode)
188189
}
190+
191+
// Test default User Agent
192+
func TestOAuthClient_DefaultUserAgent(t *testing.T) {
193+
// Assert default User Agent
194+
mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
195+
assert.NotNil(t, r.Header.Get("User-Agent"))
196+
assert.Equal(t, core.DefaultUserAgent, r.Header.Get("User-Agent"))
197+
w.WriteHeader(http.StatusOK)
198+
}))
199+
defer mockServer.Close()
200+
201+
mockCache := &MockTokenCache{}
202+
tokenSource := NewTokenSourceWithOptions(AtlasTokenSourceOptions{
203+
ClientID: "clientID",
204+
ClientSecret: "clientSecret",
205+
TokenCache: mockCache,
206+
BaseURL: &mockServer.URL,
207+
})
208+
209+
_, _ = tokenSource.GetValidToken()
210+
}
211+
212+
// Test custom User Agent
213+
func TestOAuthClient_CustomUserAgent(t *testing.T) {
214+
const customUserAgent = "/testing"
215+
216+
// Assert custom User Agent
217+
mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
218+
assert.NotNil(t, r.Header.Get("User-Agent"))
219+
assert.Equal(t, customUserAgent, r.Header.Get("User-Agent"))
220+
w.WriteHeader(http.StatusOK)
221+
}))
222+
defer mockServer.Close()
223+
224+
mockCache := &MockTokenCache{}
225+
tokenSource := NewTokenSourceWithOptions(AtlasTokenSourceOptions{
226+
ClientID: "clientID",
227+
ClientSecret: "clientSecret",
228+
UserAgent: customUserAgent,
229+
TokenCache: mockCache,
230+
BaseURL: &mockServer.URL,
231+
})
232+
233+
_, _ = tokenSource.GetValidToken()
234+
}

internal/core/constants.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package core
2+
3+
import (
4+
"runtime"
5+
)
6+
7+
const (
8+
// DefaultCloudURL is default base URL for the services.
9+
DefaultCloudURL = "https://cloud.mongodb.com"
10+
// ClientName of the v2 API client.
11+
ClientName = "go-atlas-sdk-admin"
12+
// DefaultUserAgent is default user agent header.
13+
DefaultUserAgent = ClientName + "/" + Version + " (" + runtime.GOOS + ";" + runtime.GOARCH + ")"
14+
)

tools/config/go-templates/atlas_client.mustache

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,34 +2,27 @@
22

33
import (
44
"errors"
5-
"fmt"
65
"net/http"
7-
"runtime"
86
"strings"
97

108
"github.com/mongodb-forks/digest"
119
"go.mongodb.org/atlas-sdk/v20240805005/internal/core"
1210
)
1311

1412
const (
15-
// DefaultCloudURL is default base URL for the services.
16-
DefaultCloudURL = "https://cloud.mongodb.com"
1713
// Version the version of the current API client inherited from.
1814
Version = core.Version
19-
// ClientName of the v2 API client.
20-
ClientName = "go-atlas-sdk-admin"
2115
)
2216

2317
// NewClient returns a new API Client.
2418
func NewClient(modifiers ...ClientModifier) (*APIClient, error) {
25-
userAgent := fmt.Sprintf("%s/%s (%s;%s)", ClientName, Version, runtime.GOOS, runtime.GOARCH)
2619
defaultConfig := &Configuration{
2720
HTTPClient: http.DefaultClient,
2821
Servers: ServerConfigurations{ServerConfiguration{
29-
URL: DefaultCloudURL,
22+
URL: core.DefaultCloudURL,
3023
},
3124
},
32-
UserAgent: userAgent,
25+
UserAgent: core.DefaultUserAgent,
3326
}
3427
for _, modifierFunction := range modifiers {
3528
err := modifierFunction(defaultConfig)
@@ -89,7 +82,7 @@ func UseDebug(debug bool) ClientModifier {
8982
func UseBaseURL(baseURL string) ClientModifier {
9083
return func(c *Configuration) error {
9184
if baseURL == "" {
92-
baseURL = DefaultCloudURL
85+
baseURL = core.DefaultCloudURL
9386
}
9487
urlWithoutSuffix := strings.TrimSuffix(baseURL, "/")
9588
c.Servers = ServerConfigurations{ServerConfiguration{

0 commit comments

Comments
 (0)