Skip to content

Commit

Permalink
chore(gatsby-source-contentful): migrate to latest Contentful SDK (#3…
Browse files Browse the repository at this point in the history
…5501)

* 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 <mathews.kyle@gmail.com>
  • Loading branch information
axe312ger and KyleAMathews committed Jul 25, 2022
1 parent 82172de commit 40073f8
Show file tree
Hide file tree
Showing 5 changed files with 104 additions and 102 deletions.
2 changes: 1 addition & 1 deletion e2e-tests/contentful/snapshots.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 1 addition & 4 deletions packages/gatsby-source-contentful/package.json
Expand Up @@ -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",
Expand All @@ -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"
},
Expand Down
Expand Up @@ -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()
Expand Down Expand Up @@ -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
Expand Down
120 changes: 71 additions & 49 deletions packages/gatsby-source-contentful/src/fetch.js
Expand Up @@ -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<string, unknown>
* !request?: Record<string, unknown>
* }
*/
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({
Expand Down
45 changes: 29 additions & 16 deletions yarn.lock
Expand Up @@ -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"
Expand Down Expand Up @@ -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"

Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -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"
Expand Down

0 comments on commit 40073f8

Please sign in to comment.