-
Notifications
You must be signed in to change notification settings - Fork 10.3k
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
feat(gatsby-source-drupal): Use Got instead of Axios for retries & caching support + restrict to 20 concurrent requests #31514
Merged
Merged
Changes from all commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
27a70b2
feat(gatsby-source-drupal): Use Got instead of Axios for retries & ca…
KyleAMathews abc72a1
fix lint
KyleAMathews f2e6aa4
Update yarn.lock
KyleAMathews e3848fa
Add keep-alive http agent
KyleAMathews 317c637
Merge branch 'master' into got-drupal
KyleAMathews 37baebd
Fix tests
KyleAMathews 0f506d8
yarn.lock updates
KyleAMathews a569bbe
WIP
KyleAMathews 1f1e7f2
5 seems to the most stable value for concurrency & fastest for uncach…
KyleAMathews 6666858
Merge remote-tracking branch 'origin/master' into got-drupal
KyleAMathews 63a6d26
Update yarn.lock
KyleAMathews 1597055
Update renovate.json
KyleAMathews 41a3d18
Remove logging code
KyleAMathews 1a9d72d
revert some unwanted changes
KyleAMathews 7c9f2fb
Add option for setting concurrency for API requests. Based on @Auspic…
KyleAMathews 10e9f87
Use Gatsby's cache so it's persisted to disk in between builds
KyleAMathews File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,9 @@ | ||
const axios = require(`axios`) | ||
const got = require(`got`) | ||
const _ = require(`lodash`) | ||
const urlJoin = require(`url-join`) | ||
import HttpAgent from "agentkeepalive" | ||
|
||
const { HttpsAgent } = HttpAgent | ||
|
||
const { setOptions, getOptions } = require(`./plugin-options`) | ||
|
||
|
@@ -12,6 +15,17 @@ const { | |
} = require(`./normalize`) | ||
const { handleReferences, handleWebhookUpdate } = require(`./utils`) | ||
|
||
const agent = { | ||
http: new HttpAgent(), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add an optimized http agent with https://www.npmjs.com/package/agentkeepalive |
||
https: new HttpsAgent(), | ||
} | ||
|
||
async function worker([url, options]) { | ||
return got(url, { agent, ...options }) | ||
} | ||
|
||
const requestQueue = require(`fastq`).promise(worker, 20) | ||
|
||
const asyncPool = require(`tiny-async-pool`) | ||
const bodyParser = require(`body-parser`) | ||
|
||
|
@@ -53,12 +67,18 @@ exports.sourceNodes = async ( | |
const { | ||
baseUrl, | ||
apiBase = `jsonapi`, | ||
basicAuth, | ||
basicAuth = {}, | ||
filters, | ||
headers, | ||
params, | ||
params = {}, | ||
concurrentFileRequests = 20, | ||
disallowedLinkTypes = [`self`, `describedby`], | ||
concurrentAPIRequests = 20, | ||
disallowedLinkTypes = [ | ||
`self`, | ||
`describedby`, | ||
`contact_message--feedback`, | ||
`contact_message--personal`, | ||
], | ||
skipFileDownloads = false, | ||
fastBuilds = false, | ||
entityReferenceRevisions = [], | ||
|
@@ -70,6 +90,9 @@ exports.sourceNodes = async ( | |
} = pluginOptions | ||
const { createNode, setPluginStatus, touchNode } = actions | ||
|
||
// Update the concurrency limit from the plugin options | ||
requestQueue.concurrency = concurrentAPIRequests | ||
|
||
if (webhookBody && Object.keys(webhookBody).length) { | ||
const changesActivity = reporter.activityTimer( | ||
`loading Drupal content changes`, | ||
|
@@ -139,19 +162,22 @@ exports.sourceNodes = async ( | |
|
||
try { | ||
// Hit fastbuilds endpoint with the lastFetched date. | ||
const data = await axios.get( | ||
const res = await requestQueue.push([ | ||
urlJoin(baseUrl, `gatsby-fastbuilds/sync/`, lastFetched.toString()), | ||
{ | ||
auth: basicAuth, | ||
username: basicAuth.username, | ||
password: basicAuth.password, | ||
headers, | ||
params, | ||
} | ||
) | ||
searchParams: params, | ||
responseType: `json`, | ||
cache, | ||
}, | ||
]) | ||
|
||
if (data.data.status === -1) { | ||
if (res.body.status === -1) { | ||
// The incremental data is expired or this is the first fetch. | ||
reporter.info(`Unable to pull incremental data changes from Drupal`) | ||
setPluginStatus({ lastFetched: data.data.timestamp }) | ||
setPluginStatus({ lastFetched: res.body.timestamp }) | ||
requireFullRebuild = true | ||
} else { | ||
// Touch nodes so they are not garbage collected by Gatsby. | ||
|
@@ -162,7 +188,7 @@ exports.sourceNodes = async ( | |
}) | ||
|
||
// Process sync data from Drupal. | ||
const nodesToSync = data.data.entities | ||
const nodesToSync = res.body.entities | ||
for (const nodeSyncData of nodesToSync) { | ||
if (nodeSyncData.action === `delete`) { | ||
actions.deleteNode( | ||
|
@@ -208,7 +234,7 @@ exports.sourceNodes = async ( | |
} | ||
} | ||
|
||
setPluginStatus({ lastFetched: data.data.timestamp }) | ||
setPluginStatus({ lastFetched: res.body.timestamp }) | ||
} | ||
} catch (e) { | ||
gracefullyRethrow(drupalFetchIncrementalActivity, e) | ||
|
@@ -233,13 +259,19 @@ exports.sourceNodes = async ( | |
|
||
let allData | ||
try { | ||
const data = await axios.get(urlJoin(baseUrl, apiBase), { | ||
auth: basicAuth, | ||
headers, | ||
params, | ||
}) | ||
const res = await requestQueue.push([ | ||
urlJoin(baseUrl, apiBase), | ||
{ | ||
username: basicAuth.username, | ||
password: basicAuth.password, | ||
headers, | ||
searchParams: params, | ||
responseType: `json`, | ||
cache, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Pass in Gatsby's cache object to Got to cache http requests |
||
}, | ||
]) | ||
allData = await Promise.all( | ||
_.map(data.data.links, async (url, type) => { | ||
_.map(res.body.links, async (url, type) => { | ||
if (disallowedLinkTypes.includes(type)) return | ||
if (!url) return | ||
if (!type) return | ||
|
@@ -276,31 +308,36 @@ exports.sourceNodes = async ( | |
|
||
let d | ||
try { | ||
d = await axios.get(url, { | ||
auth: basicAuth, | ||
headers, | ||
params, | ||
}) | ||
d = await requestQueue.push([ | ||
url, | ||
{ | ||
username: basicAuth.username, | ||
password: basicAuth.password, | ||
headers, | ||
responseType: `json`, | ||
cache, | ||
}, | ||
]) | ||
} catch (error) { | ||
if (error.response && error.response.status == 405) { | ||
if (error.response && error.response.statusCode == 405) { | ||
// The endpoint doesn't support the GET method, so just skip it. | ||
return [] | ||
} else { | ||
console.error(`Failed to fetch ${url}`, error.message) | ||
console.log(error.data) | ||
console.log(error) | ||
throw error | ||
} | ||
} | ||
data = data.concat(d.data.data) | ||
data = data.concat(d.body.data) | ||
// Add support for includes. Includes allow entity data to be expanded | ||
// based on relationships. The expanded data is exposed as `included` | ||
// in the JSON API response. | ||
// See https://www.drupal.org/docs/8/modules/jsonapi/includes | ||
if (d.data.included) { | ||
data = data.concat(d.data.included) | ||
if (d.body.included) { | ||
data = data.concat(d.body.included) | ||
} | ||
if (d.data.links && d.data.links.next) { | ||
data = await getNext(d.data.links.next, data) | ||
if (d.body.links && d.body.links.next) { | ||
data = await getNext(d.body.links.next, data) | ||
} | ||
|
||
return data | ||
|
@@ -485,8 +522,8 @@ exports.pluginOptionsSchema = ({ Joi }) => | |
`The path to the root of the JSONAPI — defaults to "jsonapi"` | ||
), | ||
basicAuth: Joi.object({ | ||
username: Joi.string(), | ||
password: Joi.string(), | ||
username: Joi.string().required(), | ||
password: Joi.string().required(), | ||
}).description(`Enables basicAuth`), | ||
filters: Joi.object().description( | ||
`Pass filters to the JSON API for specific collections` | ||
|
@@ -496,6 +533,7 @@ exports.pluginOptionsSchema = ({ Joi }) => | |
), | ||
params: Joi.object().description(`Append optional GET params to requests`), | ||
concurrentFileRequests: Joi.number().integer().default(20).min(1), | ||
concurrentAPIRequests: Joi.number().integer().default(20).min(1), | ||
disallowedLinkTypes: Joi.array().items(Joi.string()), | ||
skipFileDownloads: Joi.boolean(), | ||
fastBuilds: Joi.boolean(), | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
Got automatically retries requests on errors which helps the source plugin recover from occasional glitches