+
{title}
{content}
@@ -35,7 +35,7 @@ const MediaReferencePage = ({ data }) => {
})}
English Locale
{englishEntries.map(
- ({ contentful_id, title, one, oneLocalized, many, manyLocalized }) => {
+ ({ sys: { id }, title, one, oneLocalized, many, manyLocalized }) => {
const slug = slugify(title, { strict: true, lower: true })
let content = null
@@ -76,7 +76,7 @@ const MediaReferencePage = ({ data }) => {
}
return (
-
+
{title}
{content}
@@ -86,7 +86,7 @@ const MediaReferencePage = ({ data }) => {
German Locale
{germanEntries.map(
- ({ contentful_id, title, one, oneLocalized, many, manyLocalized }) => {
+ ({ sys: { id }, title, one, oneLocalized, many, manyLocalized }) => {
const slug = slugify(title, { strict: true, lower: true })
let content = null
@@ -127,7 +127,7 @@ const MediaReferencePage = ({ data }) => {
}
return (
-
+
{title}
{content}
@@ -144,11 +144,16 @@ export const pageQuery = graphql`
query MediaReferenceQuery {
default: allContentfulMediaReference(
sort: { fields: title }
- filter: { title: { glob: "!*Localized*" }, node_locale: { eq: "en-US" } }
+ filter: {
+ title: { glob: "!*Localized*" }
+ sys: { locale: { eq: "en-US" } }
+ }
) {
nodes {
title
- contentful_id
+ sys {
+ id
+ }
one {
file {
url
@@ -163,11 +168,16 @@ export const pageQuery = graphql`
}
english: allContentfulMediaReference(
sort: { fields: title }
- filter: { title: { glob: "*Localized*" }, node_locale: { eq: "en-US" } }
+ filter: {
+ title: { glob: "*Localized*" }
+ sys: { locale: { eq: "en-US" } }
+ }
) {
nodes {
title
- contentful_id
+ sys {
+ id
+ }
one {
file {
url
@@ -192,11 +202,16 @@ export const pageQuery = graphql`
}
german: allContentfulMediaReference(
sort: { fields: title }
- filter: { title: { glob: "*Localized*" }, node_locale: { eq: "de-DE" } }
+ filter: {
+ title: { glob: "*Localized*" }
+ sys: { locale: { eq: "de-DE" } }
+ }
) {
nodes {
title
- contentful_id
+ sys {
+ id
+ }
one {
file {
url
diff --git a/e2e-tests/contentful/src/pages/number.js b/e2e-tests/contentful/src/pages/number.js
index 5eff2ed704bba..946e9b71d3be1 100644
--- a/e2e-tests/contentful/src/pages/number.js
+++ b/e2e-tests/contentful/src/pages/number.js
@@ -54,8 +54,11 @@ export default NumberPage
export const pageQuery = graphql`
query NumberQuery {
default: allContentfulNumber(
- sort: { fields: contentful_id }
- filter: { title: { glob: "!*Localized*" }, node_locale: { eq: "en-US" } }
+ sort: { fields: sys___id }
+ filter: {
+ title: { glob: "!*Localized*" }
+ sys: { locale: { eq: "en-US" } }
+ }
) {
nodes {
title
@@ -64,8 +67,11 @@ export const pageQuery = graphql`
}
}
english: allContentfulNumber(
- sort: { fields: contentful_id }
- filter: { title: { glob: "*Localized*" }, node_locale: { eq: "en-US" } }
+ sort: { fields: sys___id }
+ filter: {
+ title: { glob: "*Localized*" }
+ sys: { locale: { eq: "en-US" } }
+ }
) {
nodes {
title
@@ -74,8 +80,11 @@ export const pageQuery = graphql`
}
}
german: allContentfulNumber(
- sort: { fields: contentful_id }
- filter: { title: { glob: "*Localized*" }, node_locale: { eq: "de-DE" } }
+ sort: { fields: sys___id }
+ filter: {
+ title: { glob: "*Localized*" }
+ sys: { locale: { eq: "de-DE" } }
+ }
) {
nodes {
title
diff --git a/e2e-tests/contentful/src/pages/rich-text.js b/e2e-tests/contentful/src/pages/rich-text.js
index d9d5b34d0586a..0cd2a3eca6127 100644
--- a/e2e-tests/contentful/src/pages/rich-text.js
+++ b/e2e-tests/contentful/src/pages/rich-text.js
@@ -118,7 +118,7 @@ export const pageQuery = graphql`
sort: { fields: title }
filter: {
title: { glob: "!*Localized*|*Validated*" }
- node_locale: { eq: "en-US" }
+ sys: { locale: { eq: "en-US" } }
}
) {
nodes {
@@ -128,28 +128,30 @@ export const pageQuery = graphql`
raw
references {
__typename
+ sys {
+ id
+ }
... on ContentfulAsset {
contentful_id
gatsbyImageData(width: 200)
}
... on ContentfulText {
- contentful_id
title
short
}
... on ContentfulLocation {
- contentful_id
location {
lat
lon
}
}
... on ContentfulContentReference {
- contentful_id
title
one {
__typename
- contentful_id
+ sys {
+ id
+ }
... on ContentfulText {
title
short
@@ -170,7 +172,9 @@ export const pageQuery = graphql`
}
many {
__typename
- contentful_id
+ sys {
+ id
+ }
... on ContentfulText {
title
short
@@ -200,7 +204,10 @@ export const pageQuery = graphql`
}
english: allContentfulRichText(
sort: { fields: title }
- filter: { title: { glob: "*Localized*" }, node_locale: { eq: "en-US" } }
+ filter: {
+ title: { glob: "*Localized*" }
+ sys: { locale: { eq: "en-US" } }
+ }
) {
nodes {
id
@@ -212,7 +219,10 @@ export const pageQuery = graphql`
}
german: allContentfulRichText(
sort: { fields: title }
- filter: { title: { glob: "*Localized*" }, node_locale: { eq: "de-DE" } }
+ filter: {
+ title: { glob: "*Localized*" }
+ sys: { locale: { eq: "de-DE" } }
+ }
) {
nodes {
id
diff --git a/e2e-tests/contentful/src/pages/text.js b/e2e-tests/contentful/src/pages/text.js
index 360a154201c5b..923c86e743da5 100644
--- a/e2e-tests/contentful/src/pages/text.js
+++ b/e2e-tests/contentful/src/pages/text.js
@@ -57,7 +57,7 @@ const TextPage = ({ data }) => {
Long (Plain):
-
{longEnglish.longLocalized.longLocalized}
+
{longEnglish.longLocalized.raw}
German Locale
@@ -67,7 +67,7 @@ const TextPage = ({ data }) => {
Long (Plain):
-
{longGerman.longLocalized.longLocalized}
+
{longGerman.longLocalized.raw}
)
@@ -78,28 +78,24 @@ export default TextPage
export const pageQuery = graphql`
query TextQuery {
short: contentfulText(
- node_locale: { eq: "en-US" }
- contentful_id: { eq: "5ZtcN1o7KpN7J7xgiTyaXo" }
+ sys: { id: { eq: "5ZtcN1o7KpN7J7xgiTyaXo" }, locale: { eq: "en-US" } }
) {
short
}
shortList: contentfulText(
- node_locale: { eq: "en-US" }
- contentful_id: { eq: "7b5U927WTFcQXO2Gewwa2k" }
+ sys: { id: { eq: "7b5U927WTFcQXO2Gewwa2k" }, locale: { eq: "en-US" } }
) {
shortList
}
longPlain: contentfulText(
- node_locale: { eq: "en-US" }
- contentful_id: { eq: "6ru8cSC9hZi3Ekvtw7P77S" }
+ sys: { id: { eq: "6ru8cSC9hZi3Ekvtw7P77S" }, locale: { eq: "en-US" } }
) {
longPlain {
raw
}
}
longMarkdownSimple: contentfulText(
- node_locale: { eq: "en-US" }
- contentful_id: { eq: "NyPJw0mcSuCwY2gV0zYny" }
+ sys: { id: { eq: "NyPJw0mcSuCwY2gV0zYny" }, locale: { eq: "en-US" } }
) {
longMarkdown {
childMarkdownRemark {
@@ -108,8 +104,7 @@ export const pageQuery = graphql`
}
}
longMarkdownComplex: contentfulText(
- node_locale: { eq: "en-US" }
- contentful_id: { eq: "3pwKS9UWsYmOguo4UdE1EB" }
+ sys: { id: { eq: "3pwKS9UWsYmOguo4UdE1EB" }, locale: { eq: "en-US" } }
) {
longMarkdown {
childMarkdownRemark {
@@ -118,31 +113,27 @@ export const pageQuery = graphql`
}
}
shortEnglish: contentfulText(
- node_locale: { eq: "en-US" }
- contentful_id: { eq: "2sQRyOLUexvWZj9nkzS3nN" }
+ sys: { id: { eq: "2sQRyOLUexvWZj9nkzS3nN" }, locale: { eq: "en-US" } }
) {
shortLocalized
}
shortGerman: contentfulText(
- node_locale: { eq: "de-DE" }
- contentful_id: { eq: "2sQRyOLUexvWZj9nkzS3nN" }
+ sys: { id: { eq: "2sQRyOLUexvWZj9nkzS3nN" }, locale: { eq: "de-DE" } }
) {
shortLocalized
}
longEnglish: contentfulText(
- node_locale: { eq: "en-US" }
- contentful_id: { eq: "5csovkwdDBqTKwSblAOHvd" }
+ sys: { id: { eq: "5csovkwdDBqTKwSblAOHvd" }, locale: { eq: "en-US" } }
) {
longLocalized {
- longLocalized
+ raw
}
}
longGerman: contentfulText(
- node_locale: { eq: "de-DE" }
- contentful_id: { eq: "5csovkwdDBqTKwSblAOHvd" }
+ sys: { id: { eq: "5csovkwdDBqTKwSblAOHvd" }, locale: { eq: "de-DE" } }
) {
longLocalized {
- longLocalized
+ raw
}
}
}
diff --git a/packages/gatsby-source-contentful/package.json b/packages/gatsby-source-contentful/package.json
index 1f269caddfd44..7ad571b87a54d 100644
--- a/packages/gatsby-source-contentful/package.json
+++ b/packages/gatsby-source-contentful/package.json
@@ -16,6 +16,7 @@
"chalk": "^4.1.2",
"common-tags": "^1.8.2",
"contentful": "^8.5.8",
+ "contentful-resolve-response": "^1.3.0",
"fs-extra": "^10.1.0",
"gatsby-core-utils": "^3.15.0-next.1",
"gatsby-plugin-utils": "^3.9.0-next.2",
diff --git a/packages/gatsby-source-contentful/src/__tests__/__snapshots__/gatsby-node.js.snap b/packages/gatsby-source-contentful/src/__tests__/__snapshots__/gatsby-node.js.snap
index 4961649230900..4e62cb24f807d 100644
--- a/packages/gatsby-source-contentful/src/__tests__/__snapshots__/gatsby-node.js.snap
+++ b/packages/gatsby-source-contentful/src/__tests__/__snapshots__/gatsby-node.js.snap
@@ -8,10 +8,402 @@ Array [
]
`;
-exports[`gatsby-node stores rich text as raw with references attached 2`] = `
-Array [
- "ahntqop9oi7x___7oHxo6bs0us9wIkq27qdyK___Entry___nl",
- "ahntqop9oi7x___6KpLS2NZyB3KAvDzWf4Ukh___Entry___nl",
- "ahntqop9oi7x___4ZQrqcrTunWiuNaavhGYNT___Asset___nl",
-]
+exports[`gatsby-node stores rich text as JSON 2`] = `
+Object {
+ "content": Array [
+ Object {
+ "content": Array [
+ Object {
+ "data": Object {},
+ "marks": Array [],
+ "nodeType": "text",
+ "value": "This is the homepage",
+ },
+ ],
+ "data": Object {},
+ "nodeType": "paragraph",
+ },
+ Object {
+ "content": Array [
+ Object {
+ "data": Object {},
+ "marks": Array [],
+ "nodeType": "text",
+ "value": "Heading 1",
+ },
+ ],
+ "data": Object {},
+ "nodeType": "heading-1",
+ },
+ Object {
+ "content": Array [
+ Object {
+ "data": Object {},
+ "marks": Array [],
+ "nodeType": "text",
+ "value": "Heading 2",
+ },
+ ],
+ "data": Object {},
+ "nodeType": "heading-2",
+ },
+ Object {
+ "content": Array [
+ Object {
+ "data": Object {},
+ "marks": Array [],
+ "nodeType": "text",
+ "value": "Heading 3",
+ },
+ ],
+ "data": Object {},
+ "nodeType": "heading-3",
+ },
+ Object {
+ "content": Array [
+ Object {
+ "data": Object {},
+ "marks": Array [],
+ "nodeType": "text",
+ "value": "Heading 4",
+ },
+ ],
+ "data": Object {},
+ "nodeType": "heading-4",
+ },
+ Object {
+ "content": Array [
+ Object {
+ "data": Object {},
+ "marks": Array [],
+ "nodeType": "text",
+ "value": "Heading 5",
+ },
+ ],
+ "data": Object {},
+ "nodeType": "heading-5",
+ },
+ Object {
+ "content": Array [
+ Object {
+ "data": Object {},
+ "marks": Array [],
+ "nodeType": "text",
+ "value": "Heading 6",
+ },
+ ],
+ "data": Object {},
+ "nodeType": "heading-6",
+ },
+ Object {
+ "content": Array [
+ Object {
+ "data": Object {},
+ "marks": Array [],
+ "nodeType": "text",
+ "value": "This is ",
+ },
+ Object {
+ "data": Object {},
+ "marks": Array [
+ Object {
+ "type": "bold",
+ },
+ ],
+ "nodeType": "text",
+ "value": "bold ",
+ },
+ Object {
+ "data": Object {},
+ "marks": Array [],
+ "nodeType": "text",
+ "value": "and ",
+ },
+ Object {
+ "data": Object {},
+ "marks": Array [
+ Object {
+ "type": "italic",
+ },
+ ],
+ "nodeType": "text",
+ "value": "italic",
+ },
+ Object {
+ "data": Object {},
+ "marks": Array [],
+ "nodeType": "text",
+ "value": " and ",
+ },
+ Object {
+ "data": Object {},
+ "marks": Array [
+ Object {
+ "type": "bold",
+ },
+ Object {
+ "type": "italic",
+ },
+ ],
+ "nodeType": "text",
+ "value": "both",
+ },
+ ],
+ "data": Object {},
+ "nodeType": "paragraph",
+ },
+ Object {
+ "content": Array [
+ Object {
+ "content": Array [
+ Object {
+ "content": Array [
+ Object {
+ "data": Object {},
+ "marks": Array [],
+ "nodeType": "text",
+ "value": "Very",
+ },
+ ],
+ "data": Object {},
+ "nodeType": "paragraph",
+ },
+ ],
+ "data": Object {},
+ "nodeType": "list-item",
+ },
+ Object {
+ "content": Array [
+ Object {
+ "content": Array [
+ Object {
+ "data": Object {},
+ "marks": Array [],
+ "nodeType": "text",
+ "value": "useful",
+ },
+ ],
+ "data": Object {},
+ "nodeType": "paragraph",
+ },
+ ],
+ "data": Object {},
+ "nodeType": "list-item",
+ },
+ Object {
+ "content": Array [
+ Object {
+ "content": Array [
+ Object {
+ "data": Object {},
+ "marks": Array [],
+ "nodeType": "text",
+ "value": "list",
+ },
+ ],
+ "data": Object {},
+ "nodeType": "paragraph",
+ },
+ ],
+ "data": Object {},
+ "nodeType": "list-item",
+ },
+ ],
+ "data": Object {},
+ "nodeType": "unordered-list",
+ },
+ Object {
+ "content": Array [
+ Object {
+ "content": Array [
+ Object {
+ "data": Object {},
+ "marks": Array [],
+ "nodeType": "text",
+ "value": "This is a quote",
+ },
+ ],
+ "data": Object {},
+ "nodeType": "paragraph",
+ },
+ ],
+ "data": Object {},
+ "nodeType": "blockquote",
+ },
+ Object {
+ "content": Array [
+ Object {
+ "data": Object {},
+ "marks": Array [],
+ "nodeType": "text",
+ "value": "Reference tests:",
+ },
+ ],
+ "data": Object {},
+ "nodeType": "heading-2",
+ },
+ Object {
+ "content": Array [
+ Object {
+ "data": Object {},
+ "marks": Array [],
+ "nodeType": "text",
+ "value": "Inline Link: ",
+ },
+ Object {
+ "content": Array [],
+ "data": Object {
+ "target": Object {
+ "sys": Object {
+ "id": "7oHxo6bs0us9wIkq27qdyK",
+ "linkType": "Entry",
+ "type": "Link",
+ },
+ },
+ },
+ "nodeType": "embedded-entry-inline",
+ },
+ Object {
+ "data": Object {},
+ "marks": Array [],
+ "nodeType": "text",
+ "value": "",
+ },
+ ],
+ "data": Object {},
+ "nodeType": "paragraph",
+ },
+ Object {
+ "content": Array [
+ Object {
+ "data": Object {},
+ "marks": Array [],
+ "nodeType": "text",
+ "value": "Link in list:",
+ },
+ ],
+ "data": Object {},
+ "nodeType": "paragraph",
+ },
+ Object {
+ "content": Array [
+ Object {
+ "content": Array [
+ Object {
+ "content": Array [
+ Object {
+ "data": Object {},
+ "marks": Array [],
+ "nodeType": "text",
+ "value": "",
+ },
+ Object {
+ "content": Array [],
+ "data": Object {
+ "target": Object {
+ "sys": Object {
+ "id": "6KpLS2NZyB3KAvDzWf4Ukh",
+ "linkType": "Entry",
+ "type": "Link",
+ },
+ },
+ },
+ "nodeType": "embedded-entry-inline",
+ },
+ Object {
+ "data": Object {},
+ "marks": Array [],
+ "nodeType": "text",
+ "value": "",
+ },
+ ],
+ "data": Object {},
+ "nodeType": "paragraph",
+ },
+ ],
+ "data": Object {},
+ "nodeType": "list-item",
+ },
+ ],
+ "data": Object {},
+ "nodeType": "ordered-list",
+ },
+ Object {
+ "content": Array [
+ Object {
+ "data": Object {},
+ "marks": Array [],
+ "nodeType": "text",
+ "value": "Embedded Entity:",
+ },
+ ],
+ "data": Object {},
+ "nodeType": "paragraph",
+ },
+ Object {
+ "content": Array [],
+ "data": Object {
+ "target": Object {
+ "sys": Object {
+ "id": "7oHxo6bs0us9wIkq27qdyK",
+ "linkType": "Entry",
+ "type": "Link",
+ },
+ },
+ },
+ "nodeType": "embedded-entry-block",
+ },
+ Object {
+ "content": Array [
+ Object {
+ "data": Object {},
+ "marks": Array [],
+ "nodeType": "text",
+ "value": "",
+ },
+ ],
+ "data": Object {},
+ "nodeType": "paragraph",
+ },
+ Object {
+ "content": Array [
+ Object {
+ "data": Object {},
+ "marks": Array [],
+ "nodeType": "text",
+ "value": "Embedded Asset:",
+ },
+ ],
+ "data": Object {},
+ "nodeType": "heading-2",
+ },
+ Object {
+ "content": Array [],
+ "data": Object {
+ "target": Object {
+ "sys": Object {
+ "id": "4ZQrqcrTunWiuNaavhGYNT",
+ "linkType": "Asset",
+ "type": "Link",
+ },
+ },
+ },
+ "nodeType": "embedded-asset-block",
+ },
+ Object {
+ "content": Array [
+ Object {
+ "data": Object {},
+ "marks": Array [],
+ "nodeType": "text",
+ "value": "",
+ },
+ ],
+ "data": Object {},
+ "nodeType": "paragraph",
+ },
+ ],
+ "data": Object {},
+ "nodeType": "document",
+}
`;
diff --git a/packages/gatsby-source-contentful/src/__tests__/download-contentful-assets.js b/packages/gatsby-source-contentful/src/__tests__/download-contentful-assets.js
index 72484e5b5a344..f3f6c21fdf6ee 100644
--- a/packages/gatsby-source-contentful/src/__tests__/download-contentful-assets.js
+++ b/packages/gatsby-source-contentful/src/__tests__/download-contentful-assets.js
@@ -24,24 +24,32 @@ const reporter = {
}),
}
+const mockedContentfulEntity = {
+ sys: { id: `mocked` },
+}
+
const fixtures = [
{
- sys: {
- id: `idJjXOxmNga8CSnQGEwTw`,
- type: `Asset`,
- createdAt: new Date().toISOString(),
- updatedAt: new Date().toISOString(),
+ id: `aa1beda4-b14a-50f5-89a8-222992a46a41`,
+ internal: {
+ owner: `gatsby-source-contentful`,
+ type: `ContentfulAsset`,
},
fields: {
+ title: { "en-US": `TundraUS`, fr: `TundraFR` },
file: {
- "en-US": {
- url: `//images.ctfassets.net/testing/us-image.jpeg`,
- },
+ "en-US": { url: `//images.ctfassets.net/testing/us-image.jpeg` },
+ fr: { url: `//images.ctfassets.net/testing/fr-image.jpg` },
},
},
- title: {
- "en-US": `TundraUS`,
- fr: `TundraFR`,
+ sys: {
+ id: `idJjXOxmNga8CSnQGEwTw`,
+ type: `Asset`,
+ createdAt: new Date().toISOString(),
+ updatedAt: new Date().toISOString(),
+ space: mockedContentfulEntity,
+ environment: mockedContentfulEntity,
+ revision: 123,
},
},
]
@@ -52,11 +60,7 @@ describe(`downloadContentfulAssets`, () => {
const createNodeId = jest.fn(id => id)
const defaultLocale = `en-US`
const locales = [{ code: `en-US` }, { code: `fr`, fallbackCode: `en-US` }]
- const space = {
- sys: {
- id: `1234`,
- },
- }
+ const space = mockedContentfulEntity
const cache = {
get: jest.fn(() => Promise.resolve(null)),
@@ -90,10 +94,10 @@ describe(`downloadContentfulAssets`, () => {
assetNodes.forEach(n => {
expect(cache.get).toHaveBeenCalledWith(
- `contentful-asset-${n.contentful_id}-${n.node_locale}`
+ `contentful-asset-${n.sys.id}-${n.sys.locale}`
)
expect(cache.set).toHaveBeenCalledWith(
- `contentful-asset-${n.contentful_id}-${n.node_locale}`,
+ `contentful-asset-${n.sys.id}-${n.sys.locale}`,
expect.anything()
)
})
diff --git a/packages/gatsby-source-contentful/src/__tests__/gatsby-node.js b/packages/gatsby-source-contentful/src/__tests__/gatsby-node.js
index 7505f1eb0dbc7..b23a4052c2767 100644
--- a/packages/gatsby-source-contentful/src/__tests__/gatsby-node.js
+++ b/packages/gatsby-source-contentful/src/__tests__/gatsby-node.js
@@ -160,6 +160,7 @@ describe(`gatsby-node`, () => {
getCache,
reporter,
parentSpan,
+ schema,
},
pluginOptions
)
@@ -1000,7 +1001,7 @@ describe(`gatsby-node`, () => {
`)
})
- it(`stores rich text as raw with references attached`, async () => {
+ it(`stores rich text as JSON`, async () => {
// @ts-ignore
fetchContent.mockImplementationOnce(richTextFixture.initialSync)
// @ts-ignore
@@ -1012,14 +1013,11 @@ describe(`gatsby-node`, () => {
const initNodes = getNodes()
const homeNodes = initNodes.filter(
- ({ contentful_id: id }) => id === `6KpLS2NZyB3KAvDzWf4Ukh`
+ ({ sys: { id } }) => id === `6KpLS2NZyB3KAvDzWf4Ukh`
)
expect(homeNodes).toHaveLength(2)
homeNodes.forEach(homeNode => {
- expect(homeNode.content.references___NODE).toStrictEqual([
- ...new Set(homeNode.content.references___NODE),
- ])
- expect(homeNode.content.references___NODE).toMatchSnapshot()
+ expect(homeNode.content).toMatchSnapshot()
})
})
diff --git a/packages/gatsby-source-contentful/src/__tests__/normalize.js b/packages/gatsby-source-contentful/src/__tests__/normalize.js
index 0f6ca5884f8ae..58318589a3902 100644
--- a/packages/gatsby-source-contentful/src/__tests__/normalize.js
+++ b/packages/gatsby-source-contentful/src/__tests__/normalize.js
@@ -1,11 +1,11 @@
// @ts-check
import {
buildEntryList,
- buildResolvableSet,
+ buildFallbackChain,
buildForeignReferenceMap,
- createNodesForContentType,
+ buildResolvableSet,
createAssetNodes,
- buildFallbackChain,
+ createNodesForContentType,
getLocalizedField,
makeId,
} from "../normalize"
@@ -19,19 +19,7 @@ const {
space,
} = require(`./data.json`)
-const conflictFieldPrefix = `contentful_test`
-// restrictedNodeFields from here https://www.gatsbyjs.com/docs/node-interface/
-const restrictedNodeFields = [
- `id`,
- `children`,
- `contentful_id`,
- `parent`,
- `fields`,
- `internal`,
-]
-
const pluginConfig = createPluginConfig({})
-
const unstable_createNodeManifest = jest.fn()
// Counts the created nodes per node type
@@ -200,8 +188,6 @@ describe(`Process contentful data (by name)`, () => {
contentTypeItems.forEach((contentTypeItem, i) => {
createNodesForContentType({
contentTypeItem,
- restrictedNodeFields,
- conflictFieldPrefix,
entries: entryList[i],
createNode,
createNodeId,
@@ -305,7 +291,7 @@ describe(`Process existing mutated nodes in warm build`, () => {
return {
id,
internal: {
- contentDigest: entryList[0][0].sys.updatedAt + `changed`,
+ contentDigest: entryList[0][0].sys.publishedAt + `changed`,
},
}
}
@@ -315,8 +301,6 @@ describe(`Process existing mutated nodes in warm build`, () => {
contentTypeItems.forEach((contentTypeItem, i) => {
createNodesForContentType({
contentTypeItem,
- restrictedNodeFields,
- conflictFieldPrefix,
entries: entryList[i],
createNode,
createNodeId,
@@ -419,8 +403,6 @@ describe(`Process contentful data (by id)`, () => {
contentTypeItems.forEach((contentTypeItem, i) => {
createNodesForContentType({
contentTypeItem,
- restrictedNodeFields,
- conflictFieldPrefix,
entries: entryList[i],
createNode,
createNodeId,
diff --git a/packages/gatsby-source-contentful/src/__tests__/rich-text.js b/packages/gatsby-source-contentful/src/__tests__/rich-text.js
index 46ad833a3bd55..54207938a4543 100644
--- a/packages/gatsby-source-contentful/src/__tests__/rich-text.js
+++ b/packages/gatsby-source-contentful/src/__tests__/rich-text.js
@@ -10,7 +10,7 @@ import { BLOCKS, INLINES } from "@contentful/rich-text-types"
import { initialSync } from "../__fixtures__/rich-text-data"
import { cloneDeep } from "lodash"
-const raw = JSON.stringify({
+const raw = {
nodeType: `document`,
data: {},
content: [
@@ -406,7 +406,7 @@ const raw = JSON.stringify({
data: {},
},
],
-})
+}
const fixtures = initialSync().currentSyncData
@@ -414,7 +414,6 @@ const references = [
...fixtures.entries.map(entity => {
return {
sys: entity.sys,
- contentful_id: entity.sys.id,
__typename: `ContentfulContent`,
...entity.fields,
}
@@ -422,7 +421,6 @@ const references = [
...fixtures.assets.map(entity => {
return {
sys: entity.sys,
- contentful_id: entity.sys.id,
__typename: `ContentfulAsset`,
...entity.fields,
}
@@ -453,11 +451,7 @@ describe(`rich text`, () => {
)
}
- return (
-
- Resolved inline Entry ({node.data.target.contentful_id})
-
- )
+ return
Resolved inline Entry ({node.data.target.sys.id})
},
[INLINES.ENTRY_HYPERLINK]: node => {
if (!node.data.target) {
@@ -468,9 +462,7 @@ describe(`rich text`, () => {
)
}
return (
-
- Resolved entry Hyperlink ({node.data.target.contentful_id})
-
+
Resolved entry Hyperlink ({node.data.target.sys.id})
)
},
[INLINES.ASSET_HYPERLINK]: node => {
@@ -482,9 +474,7 @@ describe(`rich text`, () => {
)
}
return (
-
- Resolved asset Hyperlink ({node.data.target.contentful_id})
-
+
Resolved asset Hyperlink ({node.data.target.sys.id})
)
},
[BLOCKS.EMBEDDED_ENTRY]: node => {
@@ -496,7 +486,7 @@ describe(`rich text`, () => {
return (
Resolved embedded Entry: {node.data.target.title[`en-US`]} (
- {node.data.target.contentful_id})
+ {node.data.target.sys.id})
)
},
@@ -509,7 +499,7 @@ describe(`rich text`, () => {
return (
Resolved embedded Asset: {node.data.target.title[`en-US`]} (
- {node.data.target.contentful_id})
+ {node.data.target.sys.id})
)
},
diff --git a/packages/gatsby-source-contentful/src/config.js b/packages/gatsby-source-contentful/src/config.js
new file mode 100644
index 0000000000000..2ed916b36e560
--- /dev/null
+++ b/packages/gatsby-source-contentful/src/config.js
@@ -0,0 +1,12 @@
+export const conflictFieldPrefix = `contentful`
+
+export const restrictedNodeFields = [
+ // restrictedNodeFields from here https://www.gatsbyjs.org/docs/node-interface/
+ `id`,
+ `children`,
+ `parent`,
+ `fields`,
+ `internal`,
+ // Contentful Common resource attributes: https://www.contentful.com/developers/docs/references/content-delivery-api/#/introduction/common-resource-attributes
+ `sys`,
+]
diff --git a/packages/gatsby-source-contentful/src/download-contentful-assets.js b/packages/gatsby-source-contentful/src/download-contentful-assets.js
index a6c0c28d0e653..f6d7f6f82ca0d 100644
--- a/packages/gatsby-source-contentful/src/download-contentful-assets.js
+++ b/packages/gatsby-source-contentful/src/download-contentful-assets.js
@@ -50,9 +50,12 @@ export async function downloadContentfulAssets(gatsbyFunctions) {
await distributeWorkload(
assetNodes.map(node => async () => {
let fileNodeID
- const { contentful_id: id, node_locale: locale } = node
+ const {
+ sys: { id, locale },
+ } = node
const remoteDataCacheKey = `contentful-asset-${id}-${locale}`
const cacheRemoteData = await cache.get(remoteDataCacheKey)
+
if (!node.file) {
reporter.log(id, locale)
reporter.warn(`The asset with id: ${id}, contains no file.`)
diff --git a/packages/gatsby-source-contentful/src/generate-schema.js b/packages/gatsby-source-contentful/src/generate-schema.js
index 141d7fbfeab6f..a10bc19538cae 100644
--- a/packages/gatsby-source-contentful/src/generate-schema.js
+++ b/packages/gatsby-source-contentful/src/generate-schema.js
@@ -108,13 +108,8 @@ function generateAssetTypes({ createTypes }) {
file: ContentfulAssetFile
title: String
description: String
- node_locale: String
- sys: ContentfulAssetSys
- contentful_id: String!
+ sys: ContentfulInternalSys
id: ID!
- spaceId: String!
- createdAt: Date @dateformat
- updatedAt: Date @dateformat
}
`)
@@ -140,13 +135,6 @@ function generateAssetTypes({ createTypes }) {
height: Int
}
`)
-
- createTypes(`
- type ContentfulAssetSys {
- type: String
- revision: Int
- }
- `)
}
export function generateSchema({
@@ -157,8 +145,8 @@ export function generateSchema({
}) {
createTypes(`
interface ContentfulInternalReference implements Node {
- contentful_id: String!
id: ID!
+ sys: ContentfulInternalSys
}
`)
@@ -173,17 +161,21 @@ export function generateSchema({
createTypes(`
type ContentfulInternalSys @dontInfer {
- type: String
- revision: Int
+ type: String!
+ id: String!
+ spaceId: String!
+ environmentId: String!
contentType: ContentfulContentType @link(by: "id", from: "contentType___NODE")
+ firstPublishedAt: Date!
+ publishedAt: Date!
+ publishedVersion: Int!
+ locale: String!
}
`)
createTypes(`
interface ContentfulEntry implements Node @dontInfer {
- contentful_id: String!
id: ID!
- spaceId: String!
sys: ContentfulInternalSys
}
`)
@@ -242,14 +234,20 @@ export function generateSchema({
}
traverse(source)
- return context.nodeModel
- .getAllNodes()
- .filter(node =>
- node.internal.owner === `gatsby-source-contentful` &&
- node.internal.type === `ContentfulAsset`
- ? referencedAssets.has(node.contentful_id)
- : referencedEntries.has(node.contentful_id)
- )
+ // Get all nodes and return all that got referenced in the rich text
+ return context.nodeModel.getAllNodes().filter(node => {
+ if (
+ !(
+ node.internal.owner === `gatsby-source-contentful` &&
+ node?.sys?.id
+ )
+ ) {
+ return false
+ }
+ return node.internal.type === `ContentfulAsset`
+ ? referencedAssets.has(node.sys.id)
+ : referencedEntries.has(node.sys.id)
+ })
},
},
},
@@ -302,15 +300,7 @@ export function generateSchema({
schema.buildObjectType({
name: makeTypeName(type),
fields: {
- contentful_id: { type: `String!` },
id: { type: `ID!` },
- // @todo reconsider the node per locale workflow
- node_locale: { type: `String!` },
- // @todo these should be real dates and in sys
- spaceId: { type: `String!` },
- createdAt: { type: `Date`, extensions: { dateformat: {} } },
- updatedAt: { type: `Date`, extensions: { dateformat: {} } },
- // @todo add metadata
sys: { type: `ContentfulInternalSys` },
...fields,
},
diff --git a/packages/gatsby-source-contentful/src/normalize.js b/packages/gatsby-source-contentful/src/normalize.js
index ce4cc2601cc89..6d2a93a3ddfb4 100644
--- a/packages/gatsby-source-contentful/src/normalize.js
+++ b/packages/gatsby-source-contentful/src/normalize.js
@@ -3,6 +3,8 @@ import _ from "lodash"
import { getGatsbyVersion } from "gatsby-core-utils"
import { lt, prerelease } from "semver"
+import { restrictedNodeFields, conflictFieldPrefix } from "./config"
+
const typePrefix = `Contentful`
export const makeTypeName = type =>
_.upperFirst(_.camelCase(`${typePrefix} ${type}`))
@@ -60,6 +62,10 @@ const makeMakeId =
(spaceId, id, type) =>
createNodeId(makeId({ spaceId, id, currentLocale, defaultLocale, type }))
+// Generates an unique id per space for reference resolving
+export const generateReferenceId = nodeOrLink =>
+ `${nodeOrLink.sys.id}___${nodeOrLink.sys.linkType || nodeOrLink.sys.type}`
+
export const buildEntryList = ({ contentTypeItems, currentSyncData }) => {
// Create buckets for each type sys.id that we care about (we will always want an array for each, even if its empty)
const map = new Map(
@@ -83,20 +89,18 @@ export const buildResolvableSet = ({
}) => {
const resolvable = new Set()
existingNodes.forEach(node => {
- // We need to add only root level resolvable (assets and entries)
- // Derived nodes (markdown or JSON) will be recreated if needed.
- resolvable.add(`${node.contentful_id}___${node.sys.type}`)
+ if (node.internal.owner === `gatsby-source-contentful` && node?.sys?.id) {
+ // We need to add only root level resolvable (assets and entries)
+ // Derived nodes (markdown or JSON) will be recreated if needed.
+ resolvable.add(generateReferenceId(node))
+ }
})
entryList.forEach(entries => {
- entries.forEach(entry =>
- resolvable.add(`${entry.sys.id}___${entry.sys.type}`)
- )
+ entries.forEach(entry => resolvable.add(generateReferenceId(entry)))
})
- assets.forEach(assetItem =>
- resolvable.add(`${assetItem.sys.id}___${assetItem.sys.type}`)
- )
+ assets.forEach(assetItem => resolvable.add(generateReferenceId(assetItem)))
return resolvable
}
@@ -137,7 +141,7 @@ export const buildForeignReferenceMap = ({
entryItemFieldValue[0].sys.id
) {
entryItemFieldValue.forEach(v => {
- const key = `${v.sys.id}___${v.sys.linkType || v.sys.type}`
+ const key = generateReferenceId(v)
// Don't create link to an unresolvable field.
if (!resolvable.has(key)) {
return
@@ -158,9 +162,7 @@ export const buildForeignReferenceMap = ({
entryItemFieldValue?.sys?.type &&
entryItemFieldValue.sys.id
) {
- const key = `${entryItemFieldValue.sys.id}___${
- entryItemFieldValue.sys.linkType || entryItemFieldValue.sys.type
- }`
+ const key = generateReferenceId(entryItemFieldValue)
// Don't create link to an unresolvable field.
if (!resolvable.has(key)) {
return
@@ -194,8 +196,8 @@ function prepareTextNode(id, node, key, text) {
type: `ContentfulNodeTypeText`,
mediaType: `text/markdown`,
content: str,
- // entryItem.sys.updatedAt is source of truth from contentful
- contentDigest: node.updatedAt,
+ // entryItem.sys.publishedAt is source of truth from contentful
+ contentDigest: node.sys.publishedAt,
},
sys: {
type: `TextNode`,
@@ -278,8 +280,6 @@ function contentfulCreateNodeManifest({
export const createNodesForContentType = ({
contentTypeItem,
- restrictedNodeFields,
- conflictFieldPrefix,
entries,
unstable_createNodeManifest,
createNode,
@@ -315,6 +315,17 @@ export const createNodesForContentType = ({
type: `${makeTypeName(`ContentType`)}`,
contentDigest: contentTypeItem.sys.updatedAt,
},
+ // https://www.contentful.com/developers/docs/references/content-delivery-api/#/introduction/common-resource-attributes
+ // https://www.contentful.com/developers/docs/references/graphql/#/reference/schema-generation/sys-field
+ sys: {
+ type: contentTypeItem.sys.type,
+ id: contentTypeItem.sys.id,
+ spaceId: contentTypeItem.sys.space.sys.id,
+ environmentId: contentTypeItem.sys.environment.sys.id,
+ firstPublishedAt: contentTypeItem.sys.createdAt,
+ publishedAt: contentTypeItem.sys.updatedAt,
+ publishedVersion: contentTypeItem.sys.revision,
+ },
}
createNodePromises.push(createNode(contentTypeNode))
@@ -392,11 +403,7 @@ export const createNodesForContentType = ({
// creating an empty node field in case when original key field value
// is empty due to links to missing entities
const resolvableEntryItemFieldValue = entryItemFieldValue
- .filter(function (v) {
- return resolvable.has(
- `${v.sys.id}___${v.sys.linkType || v.sys.type}`
- )
- })
+ .filter(v => resolvable.has(generateReferenceId(v)))
.map(function (v) {
return mId(
space.sys.id,
@@ -412,14 +419,7 @@ export const createNodesForContentType = ({
delete entryItemFields[entryItemFieldKey]
}
} else if (entryItemFieldValue?.sys?.type === `Link`) {
- if (
- resolvable.has(
- `${entryItemFieldValue.sys.id}___${
- entryItemFieldValue.sys.linkType ||
- entryItemFieldValue.sys.type
- }`
- )
- ) {
+ if (resolvable.has(generateReferenceId(entryItemFieldValue))) {
entryItemFields[`${entryItemFieldKey}___NODE`] = mId(
space.sys.id,
entryItemFieldValue.sys.id,
@@ -434,7 +434,7 @@ export const createNodesForContentType = ({
// Add reverse linkages if there are any for this node
const foreignReferences =
- foreignReferenceMap[`${entryItem.sys.id}___${entryItem.sys.type}`]
+ foreignReferenceMap[generateReferenceId(entryItem)]
if (foreignReferences) {
foreignReferences.forEach(foreignReference => {
const existingReference = entryItemFields[foreignReference.name]
@@ -465,27 +465,9 @@ export const createNodesForContentType = ({
})
}
- const sys = {
- type: entryItem.sys.type,
- }
-
- // Revision applies to entries, assets, and content types
- if (entryItem.sys.revision) {
- sys.revision = entryItem.sys.revision
- }
-
- // Content type applies to entries only
- if (entryItem.sys.contentType) {
- sys.contentType___NODE = createNodeId(contentTypeItemId)
- }
-
// Create actual entry node
let entryNode = {
id: entryNodeId,
- spaceId: space.sys.id,
- contentful_id: entryItem.sys.id,
- createdAt: entryItem.sys.createdAt,
- updatedAt: entryItem.sys.updatedAt,
parent: contentTypeItemId,
children: [],
internal: {
@@ -493,7 +475,19 @@ export const createNodesForContentType = ({
// The content of an entry is guaranteed to be updated if and only if the .sys.updatedAt field changed
contentDigest: entryItem.sys.updatedAt,
},
- sys,
+ // https://www.contentful.com/developers/docs/references/content-delivery-api/#/introduction/common-resource-attributes
+ // https://www.contentful.com/developers/docs/references/graphql/#/reference/schema-generation/sys-field
+ sys: {
+ type: entryItem.sys.type,
+ id: entryItem.sys.id,
+ locale: locale.code,
+ spaceId: entryItem.sys.space.sys.id,
+ environmentId: entryItem.sys.environment.sys.id,
+ contentType___NODE: createNodeId(contentTypeItemId),
+ firstPublishedAt: entryItem.sys.createdAt,
+ publishedAt: entryItem.sys.updatedAt,
+ publishedVersion: entryItem.sys.revision,
+ },
}
contentfulCreateNodeManifest({
@@ -504,16 +498,6 @@ export const createNodesForContentType = ({
unstable_createNodeManifest,
})
- // Revision applies to entries, assets, and content types
- if (entryItem.sys.revision) {
- entryNode.sys.revision = entryItem.sys.revision
- }
-
- // Content type applies to entries only
- if (entryItem.sys.contentType) {
- entryNode.sys.contentType = entryItem.sys.contentType
- }
-
// Replace text fields with text nodes so we can process their markdown
// into HTML.
Object.keys(entryItemFields).forEach(entryItemFieldKey => {
@@ -557,7 +541,6 @@ export const createNodesForContentType = ({
entryNode = {
...entryItemFields,
...entryNode,
- node_locale: locale.code,
}
// Link tags
@@ -614,11 +597,7 @@ export const createAssetNodes = ({
}
const assetNode = {
- contentful_id: assetItem.sys.id,
- spaceId: space.sys.id,
id: mId(space.sys.id, assetItem.sys.id, assetItem.sys.type),
- createdAt: assetItem.sys.createdAt,
- updatedAt: assetItem.sys.updatedAt,
parent: null,
children: [],
file,
@@ -626,15 +605,22 @@ export const createAssetNodes = ({
description: assetItem.fields.description
? getField(assetItem.fields.description)
: ``,
- node_locale: locale.code,
internal: {
type: `${makeTypeName(`Asset`)}`,
// The content of an asset is guaranteed to be updated if and only if the .sys.updatedAt field changed
contentDigest: assetItem.sys.updatedAt,
},
- // @todo we can probably remove this now
+ // https://www.contentful.com/developers/docs/references/content-delivery-api/#/introduction/common-resource-attributes
+ // https://www.contentful.com/developers/docs/references/graphql/#/reference/schema-generation/sys-field
sys: {
type: assetItem.sys.type,
+ id: assetItem.sys.id,
+ locale: locale.code,
+ spaceId: assetItem.sys.space.sys.id,
+ environmentId: assetItem.sys.environment.sys.id,
+ firstPublishedAt: assetItem.sys.createdAt,
+ publishedAt: assetItem.sys.updatedAt,
+ publishedVersion: assetItem.sys.revision,
},
url: `https:${file.url}`,
placeholderUrl: `https:${file.url}?w=%width%&h=%height%`,
@@ -655,14 +641,6 @@ export const createAssetNodes = ({
}
}
- // Revision applies to entries, assets, and content types
- if (assetItem.sys.revision) {
- assetNode.sys.revision = assetItem.sys.revision
- }
-
- // The content of an entry is guaranteed to be updated if and only if the .sys.updatedAt field changed
- assetNode.internal.contentDigest = assetItem.sys.updatedAt
-
// if the node hasn't changed, createNode may return `undefined` instead of a Promise on some versions of Gatsby
const maybePromise = createNode(assetNode)
diff --git a/packages/gatsby-source-contentful/src/rich-text.js b/packages/gatsby-source-contentful/src/rich-text.js
index 662914b6013f3..45e8753193aea 100644
--- a/packages/gatsby-source-contentful/src/rich-text.js
+++ b/packages/gatsby-source-contentful/src/rich-text.js
@@ -24,7 +24,7 @@ export function renderRichText({ raw, references }, options = {}) {
.map(reference => {
return {
...reference,
- sys: { type: `Entry`, id: reference.contentful_id },
+ sys: { type: `Entry`, id: reference.sys.id },
}
}),
Asset: references
@@ -32,7 +32,7 @@ export function renderRichText({ raw, references }, options = {}) {
.map(reference => {
return {
...reference,
- sys: { type: `Asset`, id: reference.contentful_id },
+ sys: { type: `Asset`, id: reference.sys.id },
}
}),
},
diff --git a/packages/gatsby-source-contentful/src/source-nodes.js b/packages/gatsby-source-contentful/src/source-nodes.js
index edeea3f6aa5e8..12530f33a8226 100644
--- a/packages/gatsby-source-contentful/src/source-nodes.js
+++ b/packages/gatsby-source-contentful/src/source-nodes.js
@@ -11,23 +11,12 @@ import {
buildResolvableSet,
createAssetNodes,
createNodesForContentType,
+ generateReferenceId,
makeId,
} from "./normalize"
import { createPluginConfig } from "./plugin-options"
import { CODES } from "./report"
-const conflictFieldPrefix = `contentful`
-
-// restrictedNodeFields from here https://www.gatsbyjs.com/docs/node-interface/
-const restrictedNodeFields = [
- `children`,
- `contentful_id`,
- `fields`,
- `id`,
- `internal`,
- `parent`,
-]
-
const CONTENT_DIGEST_COUNTER_SEPARATOR = `_COUNT_`
/***
@@ -53,7 +42,6 @@ export async function sourceNodes(
reporter,
parentSpan,
schema,
- createContentDigest,
},
pluginOptions
) {
@@ -262,7 +250,7 @@ export async function sourceNodes(
const newOrUpdatedEntries = new Set()
entryList.forEach(entries => {
entries.forEach(entry => {
- newOrUpdatedEntries.add(`${entry.sys.id}___${entry.sys.type}`)
+ newOrUpdatedEntries.add(generateReferenceId(entry))
})
})
@@ -317,22 +305,19 @@ export async function sourceNodes(
.filter(
n =>
n.sys.type === `Entry` &&
- !newOrUpdatedEntries.has(`${n.id}___${n.sys.type}`) &&
+ !newOrUpdatedEntries.has(generateReferenceId(n)) &&
!deletedEntryGatsbyReferenceIds.has(n.id)
)
.forEach(n => {
- if (
- n.contentful_id &&
- foreignReferenceMap[`${n.contentful_id}___${n.sys.type}`]
- ) {
- foreignReferenceMap[`${n.contentful_id}___${n.sys.type}`].forEach(
+ if (n.contentful_id && foreignReferenceMap[generateReferenceId(n)]) {
+ foreignReferenceMap[generateReferenceId(n)].forEach(
foreignReference => {
- const { name, id: contentfulId, type, spaceId } = foreignReference
+ const { name, id, type, spaceId } = foreignReference
const nodeId = createNodeId(
makeId({
spaceId,
- id: contentfulId,
+ id,
type,
currentLocale: n.node_locale,
defaultLocale,
@@ -465,8 +450,6 @@ export async function sourceNodes(
await Promise.all(
createNodesForContentType({
contentTypeItem,
- restrictedNodeFields,
- conflictFieldPrefix,
entries: entryList[i],
createNode,
createNodeId,
@@ -479,7 +462,6 @@ export async function sourceNodes(
useNameForId: pluginConfig.get(`useNameForId`),
pluginConfig,
unstable_createNodeManifest,
- createContentDigest,
})
)
}