-
Notifications
You must be signed in to change notification settings - Fork 751
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add an HttpClient interface and NodeHttpClient implementation. #1218
Conversation
1efb6fb
to
3f6a815
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A few questions about the TS definitions and a suggestion for how to expose this... I didn't carefully review all the implementation details yet!
types/net/net.d.ts
Outdated
* Converts the response content into a JSON object, failing if JSON | ||
* couldn't be parsed. | ||
*/ | ||
toJSON(): Promise<object>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we have a union type that's, like, all possible stripe resource types?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think so unfortunately. @richardm-stripe to confirm?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't but could easily have one if we wanted.
Though object
feels better to me here, since HTTPClient
should be mostly stripe-domain-agnostic.
const defaultHttpAgent = new http.Agent({keepAlive: true}); | ||
const defaultHttpsAgent = new https.Agent({keepAlive: true}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a reason not to move this into the constructor? It'd be nice to not have side effects on import in the case that this is being imported in a non-node environment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We do need a singleton -- if the user instantiates multiple stripe clients (without overriding the agent) then they should all share the same connection pool. We could lazily instantiate it when the constructor is called, though. Although, maybe we should instead ensure that this file is not required in non-node environments? It is NodeHttpClient
after all.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is resolved by @dcr-stripe 's latest changes
} | ||
|
||
/** Helper to make a consistent timeout error across implementations. */ | ||
static makeTimeoutError() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is great
types/net/net.d.ts
Outdated
* The streamCompleteCallback should be invoked by the response | ||
* implementation when the stream has been consumed. | ||
*/ | ||
toStream(streamCompleteCallback: () => void): any; //eslint-disable-line @typescript-eslint/no-explicit-any |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we pursue generic type parameters?
export interface HttpClient<Response> {
makeRequest(...): Promise<Response>
}
interface HttpClientResponse<RawResponse, Stream> {
...
getRawResponse(): RawResponse,
toStream(...): Stream,
...
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
test/StripeResource.spec.js
Outdated
if (err) { | ||
return done(err); | ||
} | ||
const result = await stripe.charges.create(options.data); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is missing a .catch
so if stripe.charges.create fails the error won't be properly propagated to the test runner
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
@@ -208,6 +216,98 @@ describe('StripeResource', () => { | |||
); | |||
}); | |||
|
|||
it('throws an error on invalid JSON', (done) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would there be anyway to add a test for when there's a connection error after the headers have been transmitted, but halfway through when the body is being transmitted?
(this should be pretty similar to the test case above on line #200).
That's the one case I can think of where (correct me if I'm wrong) I don't think we have any test coverage but it still would be good to know that this PR is consistent with the previous behavior (and that fetch
and nodehttpclient
will behave consistently, too)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is absolutely wonderful. In particular I think the decision to use a promise interface for HttpClient vs. more conservatively attempting to preserve the callbacky interface represents a huge improvement to the understandability of the code and will make a far better experience for implementers of this interface.
I left a few comments, though just some ideas, nothing blocking. I'll re-assign you to the PR, feel free to assign me again once you've had a look/responded and I will approve.
f73feaa
to
eabc505
Compare
eabc505
to
f68ec30
Compare
5f671af
to
e774e89
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great ideas all around. PTAL @richardm-stripe
@@ -208,6 +216,98 @@ describe('StripeResource', () => { | |||
); | |||
}); | |||
|
|||
it('throws an error on invalid JSON', (done) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
types/net/net.d.ts
Outdated
* The streamCompleteCallback should be invoked by the response | ||
* implementation when the stream has been consumed. | ||
*/ | ||
toStream(streamCompleteCallback: () => void): any; //eslint-disable-line @typescript-eslint/no-explicit-any |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
test/StripeResource.spec.js
Outdated
if (err) { | ||
return done(err); | ||
} | ||
const result = await stripe.charges.create(options.data); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, (pending fixing that TS complaint about any
)
Co-authored-by: Richard Marmorstein <52928443+richardm-stripe@users.noreply.github.com>
38e75b4
to
5ca4726
Compare
Custom request timeout message was never used since we were incorrectly requiring the HttpClient class. I believe this has stopped working on stripe#1218.
Custom request timeout message was never used since we were incorrectly requiring the HttpClient class. I believe this has stopped working on stripe#1218.
Custom request timeout message was never used since we were incorrectly requiring the HttpClient class. I believe this has stopped working on stripe#1218.
Notify
r? @richardm-stripe + @tjfontaine-stripe (for extra 👀 )
Summary
Adds an
HttpClient
interface and moves out the Nodehttp
/https
request logic into aNodeHttpClient
implementation. Update Stripe config to allow specifying a customHttpClient
.This is the first step in allowing a custom client (say one that uses fetch for #997).
The interface is summarized in the typescript. In brief:
makeRequest
returns aResponse
as a promise.Response#toStream(streamCompleteCallback)
returns a streamable version of the content. The Node behavior is unchanged here of just returning the raw response itself with the headers tacked on.Response#toJSON()
consumes the response and returns the JSON representation as a promise.This should effectively be a no-op, but this should be released as its own version in case there are issues.
You can see an example rough implementation of this for fetch in #1208.
Test plan
Added unit tests both to
StripeResource
and toNodeHttpClient
itself.