From 48a64f6d9c8f920f86b58695c9eaff43d35f5f9c Mon Sep 17 00:00:00 2001 From: Tyler Barnes Date: Thu, 30 Jun 2022 11:51:27 -0700 Subject: [PATCH] feat(gatsby-source-wordpress): Add retries to graphql requests (#35949) * add retries to graphql requests * fix test * small test refactor * Update yarn.lock --- .../__tests__/fetch-graphql.test.js | 44 ++++++++++++------- .../src/utils/fetch-graphql.ts | 36 +++++++++++---- 2 files changed, 55 insertions(+), 25 deletions(-) diff --git a/packages/gatsby-source-wordpress/__tests__/fetch-graphql.test.js b/packages/gatsby-source-wordpress/__tests__/fetch-graphql.test.js index 3d06117fe5565..81038e7eb2ae5 100644 --- a/packages/gatsby-source-wordpress/__tests__/fetch-graphql.test.js +++ b/packages/gatsby-source-wordpress/__tests__/fetch-graphql.test.js @@ -2,6 +2,19 @@ import chalk from "chalk" import fetchGraphQL, { moduleHelpers } from "../dist/utils/fetch-graphql" import store from "../dist/store" +jest.mock(`async-retry`, () => { + return { + __esModule: true, + default: jest.fn((tryFunction) => { + const bail = (e) => { + throw e + } + + return tryFunction(bail) + }) + } +}) + describe(`fetchGraphQL helper`, () => { let mock const panicMessages = [] @@ -17,12 +30,12 @@ describe(`fetchGraphQL helper`, () => { } if (query === `wpgraphql-deactivated`) { - return { + return Promise.resolve({ request: {}, headers: { [`content-type`]: `text/html`, }, - } + }) } return null @@ -55,33 +68,32 @@ describe(`fetchGraphQL helper`, () => { }) expect( - panicMessages[0].includes( - `Your WordPress server is either overloaded or encountered a PHP error.` - ) - ).toBeTruthy() + panicMessages[0] + ).toInclude(`Your WordPress server is either overloaded or encountered a PHP error.`) }) test(`handles 502, 503, and 504 errors`, async () => { + const errorMessage = `Your WordPress server at ${chalk.bold( + `fake url` + )} appears to be overloaded.` + await fetchGraphQL({ query: 502, url: `fake url`, }) + expect(panicMessages[1]).toInclude(errorMessage) + await fetchGraphQL({ query: 503, url: `fake url`, }) + expect(panicMessages[2]).toInclude(errorMessage) + await fetchGraphQL({ query: 504, url: `fake url`, }) - - const errorMessage = `Your WordPress server at ${chalk.bold( - `fake url` - )} appears to be overloaded.` - - expect(panicMessages[1].includes(errorMessage)).toBeTruthy() - expect(panicMessages[2].includes(errorMessage)).toBeTruthy() - expect(panicMessages[3].includes(errorMessage)).toBeTruthy() + expect(panicMessages[3]).toInclude(errorMessage) }) test(`errors when WPGraphQL is not active`, async () => { @@ -91,8 +103,8 @@ describe(`fetchGraphQL helper`, () => { }) expect( - panicMessages[4].includes(`Unable to connect to WPGraphQL.`) - ).toBeTruthy() + panicMessages[4] + ).toInclude(`Unable to connect to WPGraphQL.`) }) afterAll(() => { diff --git a/packages/gatsby-source-wordpress/src/utils/fetch-graphql.ts b/packages/gatsby-source-wordpress/src/utils/fetch-graphql.ts index cf1b4956608c2..a1c32b940986b 100644 --- a/packages/gatsby-source-wordpress/src/utils/fetch-graphql.ts +++ b/packages/gatsby-source-wordpress/src/utils/fetch-graphql.ts @@ -7,6 +7,7 @@ import clipboardy from "clipboardy" import axios, { AxiosRequestConfig, AxiosResponse } from "axios" import rateLimit, { RateLimitedAxiosInstance } from "axios-rate-limit" import { bold } from "chalk" +import retry from "async-retry" import { formatLogMessage } from "./format-log-message" import store from "~/store" import { getPluginOptions } from "./get-gatsby-api" @@ -27,6 +28,13 @@ export const moduleHelpers = { }, } +const errorIs500ish = (e: Error): boolean => + e.message.includes(`Request failed with status code 50`) && + (e.message.includes(`502`) || + e.message.includes(`503`) || + e.message.includes(`500`) || + e.message.includes(`504`)) + interface IHandleErrorOptionsInput { variables: IJSON query: string @@ -331,12 +339,7 @@ const handleFetchErrors = async ({ return } - if ( - e.message.includes(`Request failed with status code 50`) && - (e.message.includes(`502`) || - e.message.includes(`503`) || - e.message.includes(`504`)) - ) { + if (errorIs500ish(e) && !e.message.includes(`500`)) { if (`message` in e) { console.error(formatLogMessage(new Error(e.message).stack)) } @@ -729,9 +732,24 @@ const fetchGraphql = async ({ requestOptions.auth = htaccessCredentials } - response = await moduleHelpers - .getHttp(limit) - .post(url, { query, variables }, requestOptions) + response = await retry( + (bail: (e: Error) => void) => + moduleHelpers + .getHttp(limit) + .post(url, { query, variables }, requestOptions) + .catch(e => { + if (!errorIs500ish(e)) { + // for any error that is not a 50x error, we bail, meaning we stop retrying. error will be thrown one level higher + bail(e) + + return null + } else { + // otherwise throwing the error will cause the retry to happen again + throw e + } + }), + { retries: 5 } + ) if (response.data === ``) { throw new Error(`GraphQL request returned an empty string.`)