Skip to content

Proposal: client.WithOptions #2897

Closed
@WillAbides

Description

@WillAbides

As I was working on #2895 I realized that go-github has four different funcs for creating a new github.Client depending on what options you want to set.

  • NewClient sets the http.Client
  • NewClientWithEnvProxy builds an http.Client configured with http.ProxyFromEnvironment
  • NewTokenClient builds an http.Client configured to use bearer token auth.
  • NewEnterpriseClient is like unto NewClient but lets you set urls that point to your GHE server.

These are all fine on their own, but you can't combine them. If you want to configure a client for your enterprise server that uses token authentication, you need to choose whether to use NewTokenClient then configure BaseURL and UploadURL or configure an http.Client for token auth and pass it to NewEnterpriseClient.

Proposal

Create a new method on *Client: WithOptions(opts ...ClientOption) (*Client, error) along with options that cover the existing constructors' functionality.

The options would be:

  • WithAuthToken(token string)
  • WithEnvProxy()
  • WithEnterpriseURLs(baseURL, uploadURL string)1

Creating a new enterprise client with an auth token would look something like:

client, err := github.NewClient(nil).WithOptions(
	github.WithEnterpriseURLs(myURL, myURL),
	github.WithAuthToken(myToken), 
)

Alternatives Considered

Update NewClient to accept options

This would involve changing NewClient's signature to:

func NewClient(httpClient *http.Client, opts ...ClientOption) *Client

This would be a backward-compatible signature change because the only new argument is variadic. This would also probably be the best option if we could limit it to just this change. However, not all options are guaranteed to be valid, so we would need to either add an error to the signature or panic when an invalid option is passed. Neither of those are good compromises, so I moved on from this.

Add a "New" func

Client is the primary export of the github package, so a constructor named "New" would be a good fit semantically. If "New" doesn't work for a name, then "NewClientWithOptions" could work too.

Being a new function would also allow us to avoid the required http.Client argument and move that to an option called WithHTTPClient.

The Signature would look like: New(opts ...ClientOption) (*Client, error)

Creating a new enterprise client with an auth token would look something like:

client, err := github.New(
    github.WithEnterpriseURLs(myURL, myURL),
    github.WithAuthToken(myToken), 
)

This seems like a compelling option to me. I prefer the actual proposal over this because this issue started with an observation that we have too many constructors. Adding a new constructor in response reminded me of xkcd 927.

Separate With* methods for each option

Instead of one WithOptions method, we could add WithEnterpriseURLs, WithTokenAuth, etc.

I think a single WithOptions method lends itself to better usage. This might be a better option if none of the methods returned errors so proper method chaining could be used, but that isn't the situation here.

Footnotes

  1. I chose this over separate WithBaseURL and WithUploadURL options because this makes it more clear that they will be modified the same way NewEnterpriseClient currently modifies urls.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions