-
Notifications
You must be signed in to change notification settings - Fork 407
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
Replace deprecated requests
with node-fetch
#191
Conversation
So it seems that I've replicated all the falling tests on the node 8 build locally and it is a real problem with the library. Looking at the commit history it seems like this library had only be supporting node 10+ but then a decision was explicitly made to support node 8+. 31fd0a0 @kasrak - Do we definitely want to support node 8 moving forward? I can close this in favor of another solution if so. Sorry I didn't catch this issue earlier, but at least we have CI! |
@kasrak - So it turns out the probably wasn't with |
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.
Hm, this causes the browser build (airtable.browser.js) to go from ~200KB to 2MB which seems fishy.
Yep, the workflow is to manually generate a new airtable.browser.js as part of a release (e.g. 31fd0a0). But in this case let's keep checking in the built bundle to verify the size as part of this code review. Once we're happy with it, we'll uncheck-in those changes and land this PR without any changes to airtable.browser.js |
Use abort-controller as suggested by node-fetch https://github.com/node-fetch/node-fetch#request-cancellation-with-abortsignal When we get a request back cancel the timeout because we might need to recur. https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/clearTimeout
- Tested that `node-fetch` replacement actually works by running the `test/test_files/index.html` - In order to test againast the latest changes I regenerated the browserifyed js with `node_modules/grunt/bin/grunt browserify`. - I copied the built file to the test directly. - I'm unclear what the process is for when `airtable.browser.js` should be updated. I didn't check in the built version, because I assume that is only updated when I release is done, but I'm not clear what the process is. That would be something that would be great to clarify in the README.
- Fix sending a signal so that it actually aborts
- Remove the use of finally. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/finally#Browser_compatibility - Increase timeouts because test became flakely otherwise
@kasrak - I'm working on figuring out a solution to this. In order to move forward it would be helpful to know what browsers (and versions) this library should support. It would help me figure out what polyfills I need to add (or not) and it would help future users have a clear understanding of if the library will work for them or not. I see we are already polyfilling promise even though it has wide support (except for IE). https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise#Browser_compatibility Should that be taken as an explicate decision to support at least IE? I need to determine if I need to polyfill |
The deprecated requests module was also being used in `base#makeRequests` even though it was no longer in the list of installed packages in package.json browserify was pulling it it which was the root cause of the package being so bloated (because I was not longer replaceing requests with xhr). This replaces `request` with `node-fetch` in `makeRequest`. `makeRequest`s isn't not actually used anywhere. All API calls actually are going through `runAction` instead and I think maybe `makeRequest`s should just be removed but in order to do that we need to get more clarity around what is public vs private.
d7d6235
to
406e00f
Compare
- Regenerate the the airtable.browser.js via grunt and ensure that it works via the python server - Replace `node-fetch` and `abort-controller` packages with the build in versions on the window for the browser build - Maybe they should instead be replace by polyfills. There is a lack of clarity of what browsers needs to be supported. - Both fetch and abortController are widely supported by many browsers so I think the polyfills can be skipped. https://developer.mozilla.org/en-US/docs/Web/API/AbortController#Browser_compatibility https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API#Browser_compatibility
406e00f
to
8b92079
Compare
@kasrak - I figured out what was causing the bloat of the built package size. It turns out there was a second use of the This is now working but it would still be great to address the question I asked about what browser versions we want to support. I didn't end up pulling in pollyfills for Can you clarify what versions we support so I can determine if we need to add polyfills or not? Maybe that would also allow us to pull out the Promise polyfill library as one user already asked for. #179 I would be happy to make that change as well. Also, one reason I missed the use of Speaking of the public API, when working making changes to this library and writing tests it came up over and over again that there isn't a clear distinction documented anywhere of what is a public and what is a private interface. I think it would very helpful to the long term maintainability of the project to provide clarity around this. I would be more than happy to add jdocs to make this clear (using the public API docs as my guide) if you agree that that would be a useful path forward. Do you think it makes sense for me to go ahead and do this? (Esp ahead of the Typescript changes). |
Also, I cannot add linked issues, but this should close #153 when merged. |
Let's match the supported browser list for Airtable (https://support.airtable.com/hc/en-us/articles/217990018), which is currently:
So we can drop support for IE, drop the Promise polyfill (maybe in a separate PR?), and do a major version bump when we release the next version of Airtable.js
Let's leave it in for now, it's handy for making more custom requests to the API.
By convention, any identifiers beginning with Does any of the above require changes to this PR? Let me know if not and I'll review the code changes. Thanks! |
Yes. I have to make one more set of changes. Thanks so much for providing clear requirements on browser support. It does mean that I will need to add 2 polyfills though: one for
I'm drop the promise polyfill in a follow-up PR.
I think that is quite clear. But the API documented in the developer docs for airtable.js (the ones generated per base) only shows the use of a tiny tiny percentage of what according to the convention is technically public. It would be helpful to the long-term maintainability of the library to only commit to a small externally public API -- meaning that there would be a distinction between methods that a are private to a file (using underscore) and methods that are private to external use. Anyway, this definitely doesn't have to be answered in this PR at all. I just thought I would mention it while it was top of mind. |
- Add AbortController polyfill for Safari 10 - 11 and Firefox 52 - 56 - Was going to add fetch polyfill but it is only missing from Safari 10.0. It is in Safari 10.1 and all other browsers that need to be supported. All Safari before 10.1 is only 0.26% of all browsers usage. It is not possible to test Safari 10.0 on Browserstack and the complexity and size cost of adding the polyfill for this one minor version is large, so it was skipped it. Including the one polyfill brings the size of the built package to 232K up from 220K.
e35bfed
to
a24d8ec
Compare
@kasrak - I added the polyfill needed and tested it works with Browserstack in the older browsers, so you can review it again. |
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.
looks mostly good too me, had a few questions!
README.md
Outdated
@@ -18,6 +18,7 @@ To install airtable.js in a node project: | |||
|
|||
npm install airtable | |||
|
|||
Airtable.js should work with Node 10 and above. |
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.
nit: maybe is compatible with
instead of should work
? So the wording is a bit more confidence inspiring 😅
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.
Good call.
README.md
Outdated
@@ -33,6 +34,8 @@ Edit `test/test_files/index.html` - put your `BASE_ID` and `API_KEY` (Be careful | |||
|
|||
Then open http://localhost:8000/ in your browser. | |||
|
|||
Airtable.js should work with browsers supported by Airtable App. |
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 compatible with
and Airtable web app
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.
Fixed as well.
test/test_files/airtable.browser.js
Outdated
@@ -1,4 +1,17 @@ | |||
require=(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){ |
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.
remember to revert these changes before landing!
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've reverted it.
@@ -346,32 +355,26 @@ describe('Base', function() { | |||
}); | |||
|
|||
it('retries 429s until success', function() { | |||
const realSetTimeout = setTimeout; | |||
|
|||
jest.useFakeTimers(); |
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.
why was this changed? & number of requests changed from 3 to 2
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.
It was a few weeks ago, so I'm not 100% sure. But I think I just changed it from 2 to 3 requests to make the tests faster.
As work that procceded, I wrote a bunch of new timeout tests (to get 100% coverage) that use the actual timeouts and I think I just made this test more consistent with the other.
I think I did this work because I ran into an issue with how I passed the timeout signal with the new node fetch library which caused me to dig in with these tests more.
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.
Ahh i see - sounds reasonable, thanks!
if ('body' in options && _canRequestMethodIncludeBody(method)) { | ||
requestOptions.body = options.body; | ||
requestOptions.body = JSON.stringify(options.body); |
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.
im assuming the JSON.stringify
was previously handled by the request library?
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.
It was.
test/test_helpers.js
Outdated
@@ -143,6 +143,7 @@ function getMockEnvironmentAsync(options) { | |||
airtable: new Airtable({ | |||
apiKey: 'key123', | |||
endpointUrl: 'http://localhost:' + testServerPort, | |||
requestTimeout: 1000, |
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.
nit: maybe add a comment mentioning this is required for the timeout tests
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.
@minicat - Thanks for taking a look. I think I addressed all your comments and answered your questions now. Also, if this is ready to land. I'm 100% sure that I don't have permission do it and someone will need to do it internally. |
@@ -346,32 +355,26 @@ describe('Base', function() { | |||
}); | |||
|
|||
it('retries 429s until success', function() { | |||
const realSetTimeout = setTimeout; | |||
|
|||
jest.useFakeTimers(); |
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.
Ahh i see - sounds reasonable, thanks!
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.
thanks! A few spelling nits but otherwise looks good to go
Co-authored-by: Emma Yeap <[email protected]>
Co-authored-by: Emma Yeap <[email protected]>
@minicat - I committed spelling/grammar changes you caught. Thanks! |
- Bodys sent with HEAD/GET requests should not cause TypeErrors instead just add a console.warning.
`runAction` exposes a `Response` object in its callback. The old `request` library had a slightly different interface than the one for the `Response` object for `node-fetch`. Specifically, both have `body` defined by `request` had `body` as the parsed json body. Add the parsed json body to the `Response` object returned by runAction. `body` has no `setter` and only a `getter` so a new object needs to be created instead.
The Promise polyfill is not needed with the outlined techinal requirements for node or browser support. Closes Airtable#179 A follow up to: Airtable#191
The Promise polyfill is not needed with the outlined techinal requirements for node or browser support. Closes Airtable/airtable.js#179 A follow up to: Airtable/airtable.js#191
Requests has been deprecated: Request’s Past, Present and Future request/request#3142
There are lot of options for replacements: Alternative libraries to request request/request#3143
Went with
node-fetch
because it is the most lightweight: https://github.com/node-fetch/node-fetchUse abort-controller to add back the timeout
https://github.com/node-fetch/node-fetch#request-cancellation-with-abortsignal
When we get a request back cancel the timeout because we might need to
recur. https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/clearTimeout
Tested that
node-fetch
replacement actually works end-to-end by running thetest/test_files/index.html
In order to test against the latest changes I regenerated the
browserify
ed js withnode_modules/grunt/bin/grunt browserify
and copied the built file to the test directly. I'm unclear what the process should be for whenairtable.browser.js
should be updated. I didn't check in the built version, because I assume that is only updated when I release is done. And explanation of how and when to regenerate theairtable.browser.js
would be great to add to the README, but I'm not sure what the process is.