Use Credentials struct for NewClient

Signed-off-by: Vartan Benohanian <vartanbeno@gmail.com>
This commit is contained in:
Vartan Benohanian 2021-01-24 22:51:27 -05:00
parent 78dc97a8d5
commit 6d615771cb
6 changed files with 34 additions and 52 deletions

View file

@ -46,8 +46,8 @@ package main
import "github.com/vartanbeno/go-reddit/reddit" import "github.com/vartanbeno/go-reddit/reddit"
func main() { func main() {
withCredentials := reddit.WithCredentials("id", "secret", "username", "password") credentials := reddit.Credentials{ID: "id", Secret: "secret", Username: "username", Password: "password"}
client, _ := reddit.NewClient(withCredentials) client, _ := reddit.NewClient(credentials)
} }
``` ```
@ -55,7 +55,7 @@ You can pass in a number of options to `NewClient` to further configure the clie
```go ```go
httpClient := &http.Client{Timeout: time.Second * 30} httpClient := &http.Client{Timeout: time.Second * 30}
client, _ := reddit.NewClient(withCredentials, reddit.WithHTTPClient(httpClient)) client, _ := reddit.NewClient(credentials, reddit.WithHTTPClient(httpClient))
``` ```
### Read-Only Mode ### Read-Only Mode
@ -69,10 +69,10 @@ client, _ := reddit.NewReadonlyClient()
## Examples ## Examples
<details> <details>
<summary>Configure the client from environment variables.</summary> <summary>Configure the client from environment variables. When using this option, it's ok to pass an empty struct for the credentials.</summary>
```go ```go
client, _ := reddit.NewClient(reddit.FromEnv) client, _ := reddit.NewClient(reddit.Credentials{}, reddit.FromEnv)
``` ```
</details> </details>

View file

@ -17,9 +17,8 @@ func main() {
} }
func run() (err error) { func run() (err error) {
withCredentials := reddit.WithCredentials("id", "secret", "username", "password") credentials := reddit.Credentials{ID: "id", Secret: "secret", Username: "username", Password: "password"}
client, err := reddit.NewClient(credentials)
client, err := reddit.NewClient(withCredentials)
if err != nil { if err != nil {
return return
} }

View file

@ -7,20 +7,9 @@ import (
"os" "os"
) )
// Opt is a configuration option to initialize a client. // Opt is used to further configure a client upon initialization.
type Opt func(*Client) error type Opt func(*Client) error
// WithCredentials sets the credentials used to authenticate with the Reddit API.
func WithCredentials(id, secret, username, password string) Opt {
return func(c *Client) error {
c.ID = id
c.Secret = secret
c.Username = username
c.Password = password
return nil
}
}
// WithHTTPClient sets the HTTP client which will be used to make requests. // WithHTTPClient sets the HTTP client which will be used to make requests.
func WithHTTPClient(httpClient *http.Client) Opt { func WithHTTPClient(httpClient *http.Client) Opt {
return func(c *Client) error { return func(c *Client) error {

View file

@ -10,53 +10,44 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func TestWithCredentials(t *testing.T) {
c, err := NewClient(WithCredentials("id1", "secret1", "username1", "password1"))
require.NoError(t, err)
require.Equal(t, "id1", c.ID)
require.Equal(t, "secret1", c.Secret)
require.Equal(t, "username1", c.Username)
require.Equal(t, "password1", c.Password)
}
func TestWithHTTPClient(t *testing.T) { func TestWithHTTPClient(t *testing.T) {
_, err := NewClient(WithHTTPClient(nil)) _, err := NewClient(Credentials{}, WithHTTPClient(nil))
require.EqualError(t, err, "*http.Client: cannot be nil") require.EqualError(t, err, "*http.Client: cannot be nil")
_, err = NewClient(WithHTTPClient(&http.Client{})) _, err = NewClient(Credentials{}, WithHTTPClient(&http.Client{}))
require.NoError(t, err) require.NoError(t, err)
} }
func TestWithUserAgent(t *testing.T) { func TestWithUserAgent(t *testing.T) {
c, err := NewClient(WithUserAgent("test")) c, err := NewClient(Credentials{}, WithUserAgent("test"))
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, "test", c.UserAgent()) require.Equal(t, "test", c.UserAgent())
c, err = NewClient(WithUserAgent("")) c, err = NewClient(Credentials{}, WithUserAgent(""))
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, fmt.Sprintf("golang:%s:v%s", libraryName, libraryVersion), c.UserAgent()) require.Equal(t, fmt.Sprintf("golang:%s:v%s", libraryName, libraryVersion), c.UserAgent())
} }
func TestWithBaseURL(t *testing.T) { func TestWithBaseURL(t *testing.T) {
c, err := NewClient(WithBaseURL(":")) c, err := NewClient(Credentials{}, WithBaseURL(":"))
urlErr, ok := err.(*url.Error) urlErr, ok := err.(*url.Error)
require.True(t, ok) require.True(t, ok)
require.Equal(t, "parse", urlErr.Op) require.Equal(t, "parse", urlErr.Op)
baseURL := "http://localhost:8080" baseURL := "http://localhost:8080"
c, err = NewClient(WithBaseURL(baseURL)) c, err = NewClient(Credentials{}, WithBaseURL(baseURL))
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, baseURL, c.BaseURL.String()) require.Equal(t, baseURL, c.BaseURL.String())
} }
func TestWithTokenURL(t *testing.T) { func TestWithTokenURL(t *testing.T) {
c, err := NewClient(WithTokenURL(":")) c, err := NewClient(Credentials{}, WithTokenURL(":"))
urlErr, ok := err.(*url.Error) urlErr, ok := err.(*url.Error)
require.True(t, ok) require.True(t, ok)
require.Equal(t, "parse", urlErr.Op) require.Equal(t, "parse", urlErr.Op)
tokenURL := "http://localhost:8080/api/v1/access_token" tokenURL := "http://localhost:8080/api/v1/access_token"
c, err = NewClient(WithTokenURL(tokenURL)) c, err = NewClient(Credentials{}, WithTokenURL(tokenURL))
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, tokenURL, c.TokenURL.String()) require.Equal(t, tokenURL, c.TokenURL.String())
} }
@ -74,7 +65,7 @@ func TestFromEnv(t *testing.T) {
os.Setenv("GO_REDDIT_CLIENT_PASSWORD", "password1") os.Setenv("GO_REDDIT_CLIENT_PASSWORD", "password1")
defer os.Unsetenv("GO_REDDIT_CLIENT_PASSWORD") defer os.Unsetenv("GO_REDDIT_CLIENT_PASSWORD")
c, err := NewClient(FromEnv) c, err := NewClient(Credentials{}, FromEnv)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, "id1", c.ID) require.Equal(t, "id1", c.ID)
require.Equal(t, "secret1", c.Secret) require.Equal(t, "secret1", c.Secret)

View file

@ -21,7 +21,7 @@ import (
const ( const (
libraryName = "github.com/vartanbeno/go-reddit" libraryName = "github.com/vartanbeno/go-reddit"
libraryVersion = "1.0.0" libraryVersion = "2.0.0"
defaultBaseURL = "https://oauth.reddit.com" defaultBaseURL = "https://oauth.reddit.com"
defaultBaseURLReadonly = "https://reddit.com" defaultBaseURLReadonly = "https://reddit.com"
@ -49,7 +49,7 @@ func DefaultClient() *Client {
// RequestCompletionCallback defines the type of the request callback function. // RequestCompletionCallback defines the type of the request callback function.
type RequestCompletionCallback func(*http.Request, *http.Response) type RequestCompletionCallback func(*http.Request, *http.Response)
// Credentials used to authenticate to make requests to the Reddit API. // Credentials are used to authenticate to make requests to the Reddit API.
type Credentials struct { type Credentials struct {
ID string ID string
Secret string Secret string
@ -110,7 +110,7 @@ func newClient() *Client {
baseURL, _ := url.Parse(defaultBaseURL) baseURL, _ := url.Parse(defaultBaseURL)
tokenURL, _ := url.Parse(defaultTokenURL) tokenURL, _ := url.Parse(defaultTokenURL)
client := &Client{BaseURL: baseURL, TokenURL: tokenURL} client := &Client{client: &http.Client{}, BaseURL: baseURL, TokenURL: tokenURL}
client.Account = &AccountService{client: client} client.Account = &AccountService{client: client}
client.Collection = &CollectionService{client: client} client.Collection = &CollectionService{client: client}
@ -136,20 +136,23 @@ func newClient() *Client {
} }
// NewClient returns a new Reddit API client. // NewClient returns a new Reddit API client.
// Use an Opt to configure the client credentials, such as WithCredentials or FromEnv. // Use an Opt to configure the client credentials, such as WithHTTPClient or WithUserAgent.
func NewClient(opts ...Opt) (*Client, error) { // If the FromEnv option is used with the correct environment variables, an empty struct can
// be passed in as the credentials, since they will be overridden.
func NewClient(credentials Credentials, opts ...Opt) (*Client, error) {
client := newClient() client := newClient()
client.ID = credentials.ID
client.Secret = credentials.Secret
client.Username = credentials.Username
client.Password = credentials.Password
for _, opt := range opts { for _, opt := range opts {
if err := opt(client); err != nil { if err := opt(client); err != nil {
return nil, err return nil, err
} }
} }
if client.client == nil {
client.client = &http.Client{}
}
userAgentTransport := &userAgentTransport{ userAgentTransport := &userAgentTransport{
userAgent: client.UserAgent(), userAgent: client.UserAgent(),
Base: client.client.Transport, Base: client.client.Transport,
@ -168,7 +171,7 @@ func NewClient(opts ...Opt) (*Client, error) {
// NewReadonlyClient returns a new read-only Reddit API client. // NewReadonlyClient returns a new read-only Reddit API client.
// The client will have limited access to the Reddit API. // The client will have limited access to the Reddit API.
// Options that modify credentials (such as WithCredentials or FromEnv) won't have any effect on this client. // Options that modify credentials (such as FromEnv) won't have any effect on this client.
func NewReadonlyClient(opts ...Opt) (*Client, error) { func NewReadonlyClient(opts ...Opt) (*Client, error) {
client := newClient() client := newClient()
client.BaseURL, _ = url.Parse(defaultBaseURLReadonly) client.BaseURL, _ = url.Parse(defaultBaseURLReadonly)

View file

@ -35,7 +35,7 @@ func setup(t testing.TB) (*Client, *http.ServeMux) {
}) })
client, _ := NewClient( client, _ := NewClient(
WithCredentials("id1", "secret1", "user1", "password1"), Credentials{"id1", "secret1", "user1", "password1"},
WithBaseURL(server.URL), WithBaseURL(server.URL),
WithTokenURL(server.URL+"/api/v1/access_token"), WithTokenURL(server.URL+"/api/v1/access_token"),
) )
@ -98,20 +98,20 @@ func testClientDefaults(t *testing.T, c *Client) {
} }
func TestNewClient(t *testing.T) { func TestNewClient(t *testing.T) {
c, err := NewClient() c, err := NewClient(Credentials{})
require.NoError(t, err) require.NoError(t, err)
testClientDefaults(t, c) testClientDefaults(t, c)
} }
func TestNewClient_Error(t *testing.T) { func TestNewClient_Error(t *testing.T) {
_, err := NewClient() _, err := NewClient(Credentials{})
require.NoError(t, err) require.NoError(t, err)
errorOpt := func(c *Client) error { errorOpt := func(c *Client) error {
return errors.New("foo") return errors.New("foo")
} }
_, err = NewClient(errorOpt) _, err = NewClient(Credentials{}, errorOpt)
require.EqualError(t, err, "foo") require.EqualError(t, err, "foo")
} }