feat!: Refactor client constructor to use options pattern#4201
feat!: Refactor client constructor to use options pattern#4201stevehipwell wants to merge 4 commits into
Conversation
Signed-off-by: Steve Hipwell <steve.hipwell@gmail.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #4201 +/- ##
==========================================
+ Coverage 93.71% 97.77% +4.05%
==========================================
Files 209 189 -20
Lines 19772 19035 -737
==========================================
+ Hits 18529 18611 +82
+ Misses 1046 228 -818
+ Partials 197 196 -1 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
gmlewis
left a comment
There was a problem hiding this comment.
Thank you, @stevehipwell.
Although I like the overall pattern, I have a few concerns with this PR.
First, the various fields within Client were originally exported individually, on a case-by-case basis, as users had need to access them. Suddenly unexporting all fields may wreak havoc.
Second, the clientMu was indeed needed to solve a race condition found when copying clients that were currently in-flight, if I'm remembering correctly.
Can we keep the nice builder pattern you've created here without unexporting all the fields and without removing the mutex that is protecting the client?
|
@gmlewis if you look at the code you'll see that Unexporting the values and putting them behind the options pattern to make the client immutable is core I the whole pattern. Could you provide some examples where these need changing and |
OK, I see. That sounds good to me. If you wouldn't mind please cleaning up all the non-idiomatic failures in the examples where I have marked them, that would be greatly appreciated, then we should be ready for a second LGTM+Approval. |
|
If you could also please investigate the 8 missed lines in code coverage in |
| non-preview functionality, including changes to the exported Go API surface | ||
| or behavior of the API. | ||
| non-preview functionality, including changes to the exported Go API surface | ||
| or behavior of the API. | ||
|
|
||
| * We increment the **minor version** with any backwards-compatible changes to | ||
| functionality, as well as any changes to preview functionality in the GitHub | ||
| API. GitHub makes no guarantee about the stability of preview functionality, | ||
| so neither do we consider it a stable part of the go-github API. | ||
| functionality, as well as any changes to preview functionality in the GitHub | ||
| API. GitHub makes no guarantee about the stability of preview functionality, | ||
| so neither do we consider it a stable part of the go-github API. | ||
|
|
There was a problem hiding this comment.
Why were these lines changed? It looks like they are not related to the PR's topic.
There was a problem hiding this comment.
I've modified the README and these lines weren't consistently formatted. @gmlewis has asked me to fixup files where I've made changes, so I suggest we keep this for the same reason.
| ``` | ||
|
|
||
| *Note*: In order to interact with certain APIs, for example writing a file to a repo, one must generate an installation token | ||
| _Note_: In order to interact with certain APIs, for example writing a file to a repo, one must generate an installation token |
There was a problem hiding this comment.
Unrelated change:
| _Note_: In order to interact with certain APIs, for example writing a file to a repo, one must generate an installation token | |
| *Note*: In order to interact with certain APIs, for example writing a file to a repo, one must generate an installation token |
There was a problem hiding this comment.
I've modified the README and these lines weren't consistently formatted. @gmlewis has asked me to fixup files where I've made changes, so I suggest we keep this for the same reason.
| [support-policy]: https://golang.org/doc/devel/release.html#policy | ||
|
|
||
| ## Development | ||
| ## Development ## |
There was a problem hiding this comment.
Unrelated:
| ## Development ## | |
| ## Development |
There was a problem hiding this comment.
I've modified the README and these lines weren't consistently formatted. @gmlewis has asked me to fixup files where I've made changes, so I suggest we keep this for the same reason.
| if c.client == nil { | ||
| // WithSecondaryRateLimitOptions returns a ClientOptionsFunc that configures the Client | ||
| // secondary rate limits. | ||
| func WithSecondaryRateLimitOptions(maxRetryAfterDuration time.Duration) ClientOptionsFunc { |
There was a problem hiding this comment.
"Options" seems redundant:
| func WithSecondaryRateLimitOptions(maxRetryAfterDuration time.Duration) ClientOptionsFunc { | |
| func WithSecondaryRateLimit(maxRetryAfterDuration time.Duration) ClientOptionsFunc { |
There was a problem hiding this comment.
I've created a single options function here for the secondary rate limit both because I think there may be other options required and because the singular property being set is long. I could rename it to remove the Options suffix, but would that not be confusing given that the secondary rate limit will be enabled even if this isn't passed in?
| if err != nil { | ||
| t.Fatal(err) | ||
| } | ||
| bobClient, err := clientPreconfiguredWithURLs.Clone(WithAuthToken("bob")) |
There was a problem hiding this comment.
If we change clientPreconfiguredWithURLs to aliceClient, the test will fail:
| bobClient, err := clientPreconfiguredWithURLs.Clone(WithAuthToken("bob")) | |
| bobClient, err := aliceClient.Clone(WithAuthToken("bob")) |
=== RUN TestClientCopy_leak_transport
=== PAUSE TestClientCopy_leak_transport
=== CONT TestClientCopy_leak_transport
/Users/alexandear/src/github.com/google/go-github/github/github_test.go:4291: diff mismatch (-want +got):
string(
- "Bearer bob",
+ "Bearer alice",
)
--- FAIL: TestClientCopy_leak_transport (0.00s)
But it shouldn't fail because we are overwriting Alice's auth token with Bob's one.
There was a problem hiding this comment.
The problem is that Clone(WithAuthToken()) silently uses the original client's token instead of the new one. When cloning a client that already has an auth token set and providing a different token via WithAuthToken, the new token is ignored.
There was a problem hiding this comment.
Isn't this exactly the same behaviour as the current version?
|
@gmlewis I've addressed you review comments and should have fixed the test coverage. I've also added exclusions for the examples and other code that shouldn't be be part of the coverage report which should make it easier to see what code should have been tested but hasn't been. |
| @@ -118,7 +130,10 @@ func ExamplePullRequestsService_Create() { | |||
| // github.NewClient() instead of nil. See the following documentation for more | |||
There was a problem hiding this comment.
That comment feel misleading since github.NewClient() no longer takes nil. Should we update that?
This PR refactors the client to be immutable and to use the with options pattern for construction. This is a significant change but it should reduce the overall complexity and support better UX around new capabilities and backwards compatibility (e.g.
github.WithAPIVersion("2026-03-10")orgithub.WithGHESVersion("3.15")).Clientis immutableNewClientis the only way to create a newClientClientis created if you need to create a new one you can useCloneClientconfiguration is explicitNewClientcauses an early error instead of waiting for a failure or silently doing nothingclientMuwas unnecessaryCloses #3870
Closes #3915