-
-
Notifications
You must be signed in to change notification settings - Fork 3.1k
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
Refactor API code for GitHub backend #508
Conversation
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 optimizations and modernizations overall. Just curious about use of ternary operators to supply empty arrays & objects as opposed to using an && truthiness check. However you describe those.
...headers, | ||
...(this.token ? { Authorization: `token ${ this.token }` } : {}), |
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 to include the empty {}
when there's no token as opposed to using
...(this.token && { Authorization: `token ${ this.token}` }),
I would think unless something is looking for data based on array position, removing an empty object would be preferable.
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 this will work, since ...(undefined)
and ...(false)
both throw a SymbolError
.
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.
Ah, ok.
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.
Nitpick/point of discussion: I honestly think this is less readable. Prettier and more succinct, but less readable. Thoughts?
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.
Actually I take it back, this whole thing is a massive improvement. Going to abstain from nitpicking in favor of faster 🚢ping.
}) | ||
return this.request('/user/repos').then(repos => | ||
Object.keys(repos).some( | ||
key => (repos[key].full_name === this.repo) && repos[key].permissions.push |
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.
Ooh. Wasn't aware of some
. Very useful stuff.
src/backends/github/API.js
Outdated
const contentType = response.headers.get("Content-Type"); | ||
if (contentType && contentType.match(/json/)) { | ||
return this.parseJsonResponse(response); | ||
return Promise.all([response, response.json()]); |
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.
Awesome moving to promises!
- Removes `parseJsonResponse` - Errors on non-ok responses for text responses as well as JSON responses - Adds an optional `meta` field to `APIError` in `src/valueObjects/errors/APIError.js` - Passes github error responses to `APIErrors` generated by `request` in `API.js` of github backend
8831eb3
to
6a9f6e6
Compare
Turns out I'd broken |
313522f
to
8872d18
Compare
Looks like Travis tests are failing on 8872d18:
|
@tech4him1 thanks - it was working on my machine, so I foolishly assumed it was being polyfilled. Replacing 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.
I've never looked into Lodash before, but going through this I'm definitely going to have to use this in the future. Super useful for working with data.
src/backends/github/API.js
Outdated
]; | ||
|
||
const newItems = Object.entries(fileTree).filter(([filename]) => !added[filename]); | ||
const [newFiles, newDirs] = partition(newItems, ([, file]) => this.isFile(file)); |
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.
Not following the ([, file[)
part of this. Other than that this seems like a really nice improvement.
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.
@tortilaman it's destructuring the argument and ignoring the first element. For example:
// these are equivalent
const f1 = ([ x, y ]) => y > 0
const f2 = ([, y]) => y > 0
f1(0, 1) // true
f2(0, 1) // true
f1(1, 0) // false
f2(1, 0) // false
// you can use multiple commas
const fourthElement = ([,,,el]) => el
f3([1, 2, 3, 4]) // 4
It allows us to prevent creating a name for something we never use later on. If it's obscure enough it's confusing to most people I can change it.
8872d18
to
f789f65
Compare
@tortilaman Got another big function refactored - |
cfe952f
to
a4c8f8f
Compare
Fixed a bug in my |
a4c8f8f
to
e3c1f60
Compare
I was about to create an issue about several issues related to the API calls to github, but found this pull request. I didn't see any modifications in the tests, so it wasn't very easy to figure if these will be related to any changes already fixed. I suppose many fixes might be already in via the request refactoring, anyway please test these.
For example, I had a config like this: collections:
folder: "content/sample-posts/" The console yields an error because the following request fails This configuration fixes this issue collections:
folder: "content/sample-posts" Possible improvement here could be that at least there is a simple component in the body that gives feedback to the user there is an internal error. And of course having some checks for this config entry to ensure correct request to the github API.
I'm using Gatsby, and structure is
None of these are displayed in the list of content, even when content has been created via the workflow. Repo is here if that might help. |
@kalinchernev the first issue is definitely related to how we construct requests - we should be checking to avoid doubling slashes at the beginning and end of user-supplied strings. The other problem appears to be due to the fact that each of your entries is in its own subfolder - this isn't currently supported by the CMS. You're welcome to make an issue regarding that if you want to discuss it further, but it's a bigger concern than those addressed by this PR, which is mostly focused on code cleanup. |
@kalinchernev see #513 for the related improvement. |
@erquhart thanks, I didn't open an issue as suggested, as I managed by flattening as discussed in the existing issue :) |
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.
@Benaiah solid work on this, thank you!
One place that we still need to improve is not just readability, but understandability, or clarity. Although this code is much leaner and far more self documenting, it's still very dense. We need to break stuff up and notate.
Because of said density, my review only went so deep, so it would be nice to merge this together with your testing PR. That said, LGTM.
...headers, | ||
...(this.token ? { Authorization: `token ${ this.token }` } : {}), |
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.
Nitpick/point of discussion: I honestly think this is less readable. Prettier and more succinct, but less readable. Thoughts?
return baseHeader; | ||
} | ||
|
||
parseJsonResponse(response) { |
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.
Nice fat trimmin'
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.
Update: the Git Gateway backend is now using this as well - maybe worth keeping and refactoring.
...headers, | ||
...(this.token ? { Authorization: `token ${ this.token }` } : {}), |
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.
Actually I take it back, this whole thing is a massive improvement. Going to abstain from nitpicking in favor of faster 🚢ping.
return files | ||
.map(file => ({ ...file, file: true })) | ||
.reduce((tree, file) => tree.setIn(file.path.split("/"), file), Map()) | ||
.toJS(); |
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.
😍
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.
Does it matter that the original version did not include nested keys that were empty and the new one does?
I.e. .filter(part => part)
appears to have been dropped.
src/backends/github/API.js
Outdated
}); | ||
return Promise.all([fileTreePromise, this.getBranch()]) | ||
.then(([fileTree, branchData]) => this.updateTree(branchData.commit.sha, "/", fileTree)) | ||
.then(changeTree => this.commit(options.commitMessage, changeTree)); |
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.
No need for patch?
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.
Wondering that too, was it moved to a different location?
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'm assuming it was superfluous, but wanted 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.
I don't remember removing this intentionally, so it was likely an oversight. Thanks for the catch!
timeStamp: new Date().toISOString(), | ||
}; | ||
|
||
return (unpublished |
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 lot of this really should be broken down and notated for clarity at some point in the future.
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.
Definitely agreed.
Functions refactored: - `storeMetadata` - `composeFileTree` - `persistFiles` - `uploadBlob` These refactors had to be done together, since each the methods refactored had implicit dependencies on mutations made by the others. Specifically, `composeFileTree` and `uploadBlob` had to be called in a specific order to mutate their arguments correctly. This led to a race condition in `persistFiles` that was (fortunately) only affecting debugging. This commit removes the mutation and makes the functions more independent, in addition to making general readability and code length improvements to the affected functions. `composeFileTree` has also had its filtering of uploaded files removed, as that was a concern better handled by the caller.
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.
@Benaiah I know this PR is on the back burner atm, but I think we're going to need a lot of source documentation and tests added before we can safely merge. Two reasons:
-
Succinctness and immutability are huge code improvements, but we can't risk bugs for them (hence the need for tests).
-
Things are a little easier to read here, but not by much. It isn't a fault in your refactor, it's just that there's too much going on here to rely on self-documenting. Source docs can resolve this, and will also make reviews of this PR much more informed.
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.
@Benaiah if you can address the comments, get this rebased, and we get one more approval, we can merge.
return baseHeader; | ||
} | ||
|
||
parseJsonResponse(response) { |
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.
Update: the Git Gateway backend is now using this as well - maybe worth keeping and refactoring.
return Promise.all(updates) | ||
.then(updates => this.request(`${ this.repoURL }/git/trees`, { | ||
.then(resolvedUpdates => this.request(`${ this.repoURL }/git/trees`, { | ||
method: "POST", | ||
body: JSON.stringify({ base_tree: sha, tree: updates }), |
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 still needs to be fixed.
}) | ||
.catch((error) => { | ||
throw new APIError(error.message, responseStatus, 'GitHub'); | ||
.catch(err => [err, null]) |
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 should be
.catch(err => Promise.reject([err, null))
so the other.catch
block catches and destructures it properly. This is my bad - it's the same way in my GH API refactor PR.
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.
That's correct, thanks!
? Object.entries(options.params).map( | ||
([key, val]) => `${ key }=${ encodeURIComponent(val) }`) | ||
: []; | ||
return `${ this.api_root }${ path }?${ [cacheBuster, ...encodedParams].join("&") }`; |
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.
Can we make use of modern API here (to avoid re-inventing the wheel)?
Specifically:
- URLSearchParams for the query string
- URL for the merging of parts (root, path and query string)
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.
Just a note: URLSearchParams
isn't supported in MS Edge <17. Otherwise, I think this is a great idea.
they will be removed when PR decaporg#508 is merged
Closing as stale, leaving branch deletion for @Benaiah. |
- Summary
Refactor of
API.js
in the GitHub backend. General improvements tocode style, removal of mutation, function interdependencies, and race
conditions (see the commit message for 732ede1 for more details on
that). Code modified is generally much shorter.
Other refactors I'm working on for this PR:
updateTree
editorialWorkflowGit
_
with individual function imports.UseImmutable
objects throughout to avoid having to convert to and from them in individual functions.Refine request handling (possibly removemeta
field from requesterrors if a better solution is found).EDIT: I'm closing the scope of this so we can get it merged. Further refactors can come in later PRs.
- Test plan
Tested manually, and existing tests pass.
- Description for the changelog
API.js
in GitHub backend- A picture of a cute animal (not mandatory but encouraged)