From 40073f8bbca274287b7887b3503159eb709a2987 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benedikt=20R=C3=B6tsch?= Date: Mon, 25 Jul 2022 22:56:26 +0200 Subject: [PATCH] chore(gatsby-source-contentful): migrate to latest Contentful SDK (#35501) * build: migrate to latest contentful.js * test(contenful-e2e): update snapshot version * build: clean up dependencies and update yarn.lock * fix: readd url dependency * build: upgrade to actual latest contentful js Co-authored-by: Kyle Mathews --- e2e-tests/contentful/snapshots.js | 2 +- .../gatsby-source-contentful/package.json | 5 +- .../src/__tests__/fetch-network-errors.js | 34 +---- .../gatsby-source-contentful/src/fetch.js | 120 +++++++++++------- yarn.lock | 45 ++++--- 5 files changed, 104 insertions(+), 102 deletions(-) diff --git a/e2e-tests/contentful/snapshots.js b/e2e-tests/contentful/snapshots.js index c7626ba18be77..2aeca7312618f 100644 --- a/e2e-tests/contentful/snapshots.js +++ b/e2e-tests/contentful/snapshots.js @@ -1,5 +1,5 @@ module.exports = { - "__version": "9.5.4", + "__version": "9.6.1", "content-reference": { "content-reference-many-2nd-level-loop": { "1": "
\n

Content Reference: Many (2nd level loop)

\n

[ContentfulNumber]\n 42

\n

[ContentfulText]\n The quick brown fox jumps over the lazy dog.

\n

[ContentfulReference]\n Content Reference: One (Loop A -> B)\n : [\n Content Reference: One (Loop B -> A)\n ]

\n
" diff --git a/packages/gatsby-source-contentful/package.json b/packages/gatsby-source-contentful/package.json index 71addf53b702d..e92cb1f933b1e 100644 --- a/packages/gatsby-source-contentful/package.json +++ b/packages/gatsby-source-contentful/package.json @@ -12,10 +12,9 @@ "@contentful/rich-text-types": "^15.12.1", "@hapi/joi": "^15.1.1", "@vercel/fetch-retry": "^5.1.3", - "axios": "^0.21.1", "chalk": "^4.1.2", "common-tags": "^1.8.2", - "contentful": "^8.5.8", + "contentful": "^9.1.33", "fs-extra": "^10.1.0", "gatsby-core-utils": "^3.20.0-next.0", "gatsby-plugin-utils": "^3.14.0-next.1", @@ -24,8 +23,6 @@ "json-stringify-safe": "^5.0.1", "lodash": "^4.17.21", "node-fetch": "^2.6.7", - "p-queue": "^6.6.2", - "retry-axios": "^2.6.0", "semver": "^7.3.7", "url": "^0.11.0" }, diff --git a/packages/gatsby-source-contentful/src/__tests__/fetch-network-errors.js b/packages/gatsby-source-contentful/src/__tests__/fetch-network-errors.js index d629c5966cbce..623402d38d2fb 100644 --- a/packages/gatsby-source-contentful/src/__tests__/fetch-network-errors.js +++ b/packages/gatsby-source-contentful/src/__tests__/fetch-network-errors.js @@ -108,13 +108,11 @@ describe(`fetch-retry`, () => { const msg = expect(e.context.sourceMessage) msg.toEqual( expect.stringContaining( - `Fetching contentful data failed: 500 MockedContentfulError` + `Fetching contentful data failed: ERR_BAD_RESPONSE 500 MockedContentfulError` ) ) msg.toEqual(expect.stringContaining(`Request ID: 123abc`)) - msg.toEqual( - expect.stringContaining(`The request was sent with 3 attempts`) - ) + msg.toEqual(expect.stringContaining(`Attempts: 3`)) } expect(reporter.panic).toBeCalled() expect(scope.isDone()).toBeTruthy() @@ -149,34 +147,6 @@ describe(`fetch-network-errors`, () => { expect(scope.isDone()).toBeTruthy() }) - test(`catches error with response string`, async () => { - const scope = nock(baseURI) - // Space - .get(`/spaces/${options.spaceId}/`) - .reply(502, `Bad Gateway`) - - try { - await fetchContent({ - pluginConfig: createPluginConfig({ - ...options, - contentfulClientConfig: { retryOnError: false }, - }), - reporter, - syncToken: null, - }) - throw new Error(`fetchContent should throw an error`) - } catch (e) { - expect(e.context.sourceMessage).toEqual( - expect.stringContaining( - `Accessing your Contentful space failed: Bad Gateway` - ) - ) - } - - expect(reporter.panic).toBeCalled() - expect(scope.isDone()).toBeTruthy() - }) - test(`catches error with response object`, async () => { const scope = nock(baseURI) // Space diff --git a/packages/gatsby-source-contentful/src/fetch.js b/packages/gatsby-source-contentful/src/fetch.js index 209153198fefa..d38dba1ebbee4 100644 --- a/packages/gatsby-source-contentful/src/fetch.js +++ b/packages/gatsby-source-contentful/src/fetch.js @@ -9,68 +9,90 @@ import { CODES } from "./report" * Generate a user friendly error message. * * Contentful's API has its own error message structure, which might change depending of internal server or authentification errors. - * - * Additionally the SDK strips the error object, sometimes: - * https://github.com/contentful/contentful.js/blob/b67b77ac8c919c4ec39203f8cac2043854ab0014/lib/create-contentful-api.js#L89-L99 - * - * This code tries to work around this. */ const createContentfulErrorMessage = e => { - if (typeof e === `string`) { - return e - } - // If we got a response, it is very likely that it is a Contentful API error. - if (e.response) { - let parsedContentfulErrorData = null + // Handle axios error messages + if (e.isAxiosError) { + const axiosErrorMessage = [e.code, e.status] + const axiosErrorDetails = [] - // Parse JSON response data, and add it to the object. - if (typeof e.response.data === `string`) { - try { - parsedContentfulErrorData = JSON.parse(e.response.data) - } catch (err) { - e.message = e.response.data + if (e.response) { + axiosErrorMessage.push(e.response.status) + + // Parse Contentful API error data + if (e.response?.data) { + axiosErrorMessage.push(e.response.data.sys?.id, e.response.data.message) + } + + // Get request ID from headers + const requestId = + e.response.headers && + typeof e.response.headers === `object` && + e.response.headers[`x-contentful-request-id`] + + if (requestId) { + axiosErrorDetails.push(`Request ID: ${requestId}`) } - // If response data was parsed already, just add it. - } else if (typeof e.response.data === `object`) { - parsedContentfulErrorData = e.response.data } - e = { ...e, ...e.response, ...parsedContentfulErrorData } - } + if (e.attempts) { + axiosErrorDetails.push(`Attempts: ${e.attempts}`) + } - let errorMessage = [ - // Generic error values - e.code && String(e.code), - e.status && String(e.status), - e.statusText, - // Contentful API error response values - e.sys?.id, - ] - .filter(Boolean) - .join(` `) - - // Add message if it exists. Usually error default or Contentful's error message - if (e.message) { - errorMessage += `\n\n${e.message}` - } + if (axiosErrorDetails.length) { + axiosErrorMessage.push( + `\n\n---\n${axiosErrorDetails.join(`\n\n`)}\n\n---\n` + ) + } - // Get request ID from headers or Contentful's error data - const requestId = - (e.headers && - typeof e.headers === `object` && - e.headers[`x-contentful-request-id`]) || - e.requestId + return axiosErrorMessage.filter(Boolean).join(` `) + } - if (requestId) { - errorMessage += `\n\nRequest ID: ${requestId}` + // If it is not an axios error, we assume that we got a Contentful SDK error and try to parse it + const errorMessage = [e.name] + const errorDetails = [] + try { + /** + * Parse stringified error data from message + * https://github.com/contentful/contentful-sdk-core/blob/4cfcd452ba0752237a26ce6b79d72a50af84d84e/src/error-handler.ts#L71-L75 + * + * @todo properly type this with TS + * type { + * status?: number + * statusText?: string + * requestId?: string + * message: string + * !details: Record + * !request?: Record + * } + */ + const errorData = JSON.parse(e.message) + errorMessage.push(errorData.status && String(errorData.status)) + errorMessage.push(errorData.statusText) + errorMessage.push(errorData.message) + if (errorData.requestId) { + errorDetails.push(`Request ID: ${errorData.requestId}`) + } + if (errorData.request) { + errorDetails.push( + `Request:\n${JSON.stringify(errorData.request, null, 2)}` + ) + } + if (errorData.details && Object.keys(errorData.details).length) { + errorDetails.push( + `Details:\n${JSON.stringify(errorData.details, null, 2)}` + ) + } + } catch (err) { + // If we can't parse it, we assume its a human readable string + errorMessage.push(e.message) } - // Tell the user about how many request attempts Contentful SDK made - if (e.attempts) { - errorMessage += `\n\nThe request was sent with ${e.attempts} attempts` + if (errorDetails.length) { + errorMessage.push(`\n\n---\n${errorDetails.join(`\n\n`)}\n\n---\n`) } - return errorMessage + return errorMessage.filter(Boolean).join(` `) } function createContentfulClientOptions({ diff --git a/yarn.lock b/yarn.lock index 2d4597a6d4bb1..027d5650b4f1f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6153,6 +6153,14 @@ axios@^0.21.1: dependencies: follow-redirects "^1.10.0" +axios@^0.27.0: + version "0.27.2" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.27.2.tgz#207658cc8621606e586c85db4b41a750e756d972" + integrity sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ== + dependencies: + follow-redirects "^1.14.9" + form-data "^4.0.0" + axobject-query@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-2.2.0.tgz#943d47e10c0b704aa42275e20edf3722648989be" @@ -7958,25 +7966,25 @@ contentful-resolve-response@^1.3.0: dependencies: fast-copy "^2.1.0" -contentful-sdk-core@^6.8.5: - version "6.10.3" - resolved "https://registry.yarnpkg.com/contentful-sdk-core/-/contentful-sdk-core-6.10.3.tgz#983fd69257c239881c43cb83e3ce9f501acfbe4a" - integrity sha512-IUBkAU1sJuVaEa2Nv1NKK5ImqpBZ5Q3EmaCFmMZx/UHKa+i98nDCSTUBOL1aJnpZ/s3AaSramsh73VQ4aK2kyA== +contentful-sdk-core@^7.0.1: + version "7.0.2" + resolved "https://registry.yarnpkg.com/contentful-sdk-core/-/contentful-sdk-core-7.0.2.tgz#5585880f546772246209de25256635ce31fd8d8e" + integrity sha512-HkBzzzJ3UGqOIJiTd4qMEMvn44ccrN7a75gEej28X1srGn05myRgJ/pWbmXJhtgpq/5gU7IURnynyKx/ecsOfg== dependencies: - fast-copy "^2.1.0" + fast-copy "^2.1.3" lodash.isplainobject "^4.0.6" lodash.isstring "^4.0.1" p-throttle "^4.1.1" qs "^6.9.4" -contentful@^8.5.8: - version "8.5.8" - resolved "https://registry.yarnpkg.com/contentful/-/contentful-8.5.8.tgz#ad2f3549d1795310e104a6c33325352524f7bd77" - integrity sha512-6YyE95uDJYTyGKQYtqYrMzdDZe3sLkrC0UEnpXuIOeciGACRQP9ouTjRJnLMa5ONUPt0+UJh7JH3epNouPZWIw== +contentful@^9.1.33: + version "9.1.33" + resolved "https://registry.yarnpkg.com/contentful/-/contentful-9.1.33.tgz#1305254c647578ad981eae59fae1abdb07b50b6a" + integrity sha512-iiu2cC/9JvDrTK6cfSHhZ1iW6dOq+NmYMA2p5Thpv+9h2pEOyoHm1Un9Xir5XZSB11bu4POmo6JazGAn9N0tqg== dependencies: - axios "^0.21.1" + axios "^0.27.0" contentful-resolve-response "^1.3.0" - contentful-sdk-core "^6.8.5" + contentful-sdk-core "^7.0.1" fast-copy "^2.1.0" json-stringify-safe "^5.0.1" @@ -10885,6 +10893,11 @@ fast-copy@^2.1.0: resolved "https://registry.yarnpkg.com/fast-copy/-/fast-copy-2.1.1.tgz#f5cbcf2df64215e59b8e43f0b2caabc19848083a" integrity sha512-Qod3DdRgFZ8GUIM6ygeoZYpQ0QLW9cf/FS9KhhjlYggcSZXWAemAw8BOCO5LuYCrR3Uj3qXDVTUzOUwG8C7beQ== +fast-copy@^2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/fast-copy/-/fast-copy-2.1.3.tgz#bf6e05ac3cb7a9d66fbf12c51dd4440e9ddd4afb" + integrity sha512-LDzYKNTHhD+XOp8wGMuCkY4eTxFZOOycmpwLBiuF3r3OjOmZnURRD8t2dUAbmKuXGbo/MGggwbSjcBdp8QT0+g== + fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" @@ -11257,6 +11270,11 @@ follow-redirects@^1.10.0: resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.0.tgz#b42e8d93a2a7eea5ed88633676d6597bc8e384db" integrity sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA== +follow-redirects@^1.14.9: + version "1.14.9" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.9.tgz#dd4ea157de7bfaf9ea9b3fbd85aa16951f78d8d7" + integrity sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w== + font-family-papandreou@^0.2.0-patch1, font-family-papandreou@^0.2.0-patch2: version "0.2.0-patch2" resolved "https://registry.yarnpkg.com/font-family-papandreou/-/font-family-papandreou-0.2.0-patch2.tgz#c75b659e96ffbc7ab2af651cf7b4910b334e8dd2" @@ -21815,11 +21833,6 @@ retext@^7.0.1: retext-stringify "^2.0.0" unified "^8.0.0" -retry-axios@^2.6.0: - version "2.6.0" - resolved "https://registry.yarnpkg.com/retry-axios/-/retry-axios-2.6.0.tgz#d4dc5c8a8e73982e26a705e46a33df99a28723e0" - integrity sha512-pOLi+Gdll3JekwuFjXO3fTq+L9lzMQGcSq7M5gIjExcl3Gu1hd4XXuf5o3+LuSBsaULQH7DiNbsqPd1chVpQGQ== - retry@0.12.0, retry@^0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b"