From b80c53a31348a6a6024fa36cee50a171b80f9632 Mon Sep 17 00:00:00 2001 From: GatsbyJS Bot Date: Wed, 27 Oct 2021 12:54:10 -0600 Subject: [PATCH] fix(gatsby-source-drupal): Correctly update nodes with changed back references so queries are re-run (#33328) (#33699) (cherry picked from commit 5196e40ef38660b6d8d621deb87fa719f124f1a4) Co-authored-by: Kyle Mathews Co-authored-by: Vladimir Razuvaev --- .../src/__tests__/fixtures/1593545806.json | 32 +++++++ .../src/__tests__/index.js | 4 + .../gatsby-source-drupal/src/gatsby-node.js | 1 + packages/gatsby-source-drupal/src/utils.js | 84 +++++++++++-------- 4 files changed, 86 insertions(+), 35 deletions(-) diff --git a/packages/gatsby-source-drupal/src/__tests__/fixtures/1593545806.json b/packages/gatsby-source-drupal/src/__tests__/fixtures/1593545806.json index 24d692715b313..5ec0de662b78f 100644 --- a/packages/gatsby-source-drupal/src/__tests__/fixtures/1593545806.json +++ b/packages/gatsby-source-drupal/src/__tests__/fixtures/1593545806.json @@ -35,6 +35,38 @@ } } }, + { + "jsonapi": { + "version": "1.0", + "meta": { + "links": { + "self": { + "href": "http://jsonapi.org/format/1.0/" + } + } + } + }, + "data": { + "type": "node--article", + "id": "article-9", + "attributes": { + "id": 24, + "uuid": "article-9", + "title": "Article #9 - new", + "body": "Aliquam non varius libero, sit amet consequat ex. Aenean porta turpis quis vulputate blandit. Suspendisse in porta erat. Sed sit amet scelerisque turpis, at rutrum mauris. Sed tempor eleifend lobortis. Proin maximus, massa sed dignissim sollicitudin, quam risus mattis justo, sit amet aliquam odio ligula quis urna. Interdum et malesuada fames ac ante ipsum primis in faucibus. Ut mollis leo nisi, at interdum urna fermentum ut. Fusce id suscipit neque, eu fermentum lacus. Donec egestas laoreet felis ac luctus. Vestibulum molestie mattis ante, a vulputate nunc ullamcorper at. Ut hendrerit ipsum eget gravida ultricies." + }, + "relationships": { + "field_tags": { + "data": [ + { + "type": "taxonomy_term--tags", + "id": "tag-2" + } + ] + } + } + } + }, { "jsonapi": { "version": "1.0", diff --git a/packages/gatsby-source-drupal/src/__tests__/index.js b/packages/gatsby-source-drupal/src/__tests__/index.js index ed8929323918d..a06e84cf1f3dc 100644 --- a/packages/gatsby-source-drupal/src/__tests__/index.js +++ b/packages/gatsby-source-drupal/src/__tests__/index.js @@ -574,6 +574,10 @@ describe(`gatsby-source-drupal`, () => { expect( nodes[createNodeId(`und.tag-2`)].relationships[`node__article___NODE`] ).toContain(createNodeId(`und.article-3`)) + // Created a new node article-9 with reference to tag-2 + expect( + nodes[createNodeId(`und.tag-2`)].relationships[`node__article___NODE`] + ).toContain(createNodeId(`und.article-9`)) }) }) }) diff --git a/packages/gatsby-source-drupal/src/gatsby-node.js b/packages/gatsby-source-drupal/src/gatsby-node.js index a5114c03490ad..7ca4bdb25a0ac 100644 --- a/packages/gatsby-source-drupal/src/gatsby-node.js +++ b/packages/gatsby-source-drupal/src/gatsby-node.js @@ -582,6 +582,7 @@ ${JSON.stringify(webhookBody, null, 4)}` nodes.forEach(node => { handleReferences(node, { getNode: nodes.get.bind(nodes), + mutateNode: true, createNodeId, entityReferenceRevisions, }) diff --git a/packages/gatsby-source-drupal/src/utils.js b/packages/gatsby-source-drupal/src/utils.js index 474f804e77ef7..eda40f3437987 100644 --- a/packages/gatsby-source-drupal/src/utils.js +++ b/packages/gatsby-source-drupal/src/utils.js @@ -9,16 +9,17 @@ const { const { getOptions } = require(`./plugin-options`) -const backRefsNamesLookup = new WeakMap() -const referencedNodesLookup = new WeakMap() +const backRefsNamesLookup = new Map() +const referencedNodesLookup = new Map() const handleReferences = ( node, - { getNode, createNodeId, entityReferenceRevisions = [] } + { getNode, mutateNode = false, createNodeId, entityReferenceRevisions = [] } ) => { const relationships = node.relationships const rootNodeLanguage = getOptions().languageConfig ? node.langcode : `und` + const backReferencedNodes = [] if (node.drupal_relationships) { const referencedNodes = [] _.each(node.drupal_relationships, (v, k) => { @@ -68,6 +69,7 @@ const handleReferences = ( relationships[nodeFieldName] = referencedNodeId referencedNodes.push(referencedNodeId) } + // If there's meta on the field and it's not an existing/internal one // create a new node's field with that meta. It can't exist on both // @see https://jsonapi.org/format/#document-resource-object-fields @@ -78,11 +80,16 @@ const handleReferences = ( }) delete node.drupal_relationships - referencedNodesLookup.set(node, referencedNodes) + referencedNodesLookup.set(node.id, referencedNodes) if (referencedNodes.length) { const nodeFieldName = `${node.internal.type}___NODE` referencedNodes.forEach(nodeID => { - const referencedNode = getNode(nodeID) + let referencedNode + if (mutateNode) { + referencedNode = getNode(nodeID) + } else { + referencedNode = _.cloneDeep(getNode(nodeID)) + } if (!referencedNode.relationships[nodeFieldName]) { referencedNode.relationships[nodeFieldName] = [] } @@ -91,20 +98,23 @@ const handleReferences = ( referencedNode.relationships[nodeFieldName].push(node.id) } - let backRefsNames = backRefsNamesLookup.get(referencedNode) + let backRefsNames = backRefsNamesLookup.get(referencedNode.id) if (!backRefsNames) { backRefsNames = [] - backRefsNamesLookup.set(referencedNode, backRefsNames) + backRefsNamesLookup.set(referencedNode.id, backRefsNames) } if (!backRefsNames.includes(nodeFieldName)) { backRefsNames.push(nodeFieldName) } + backReferencedNodes.push(referencedNode) }) } } node.relationships = relationships + + return backReferencedNodes } exports.handleReferences = handleReferences @@ -117,7 +127,7 @@ const handleDeletedNode = async ({ createContentDigest, entityReferenceRevisions, }) => { - const deletedNode = getNode( + let deletedNode = getNode( createNodeId( createNodeIdWithVersion( node.id, @@ -135,20 +145,25 @@ const handleDeletedNode = async ({ return deletedNode } + // Clone node so we're not mutating the original node. + deletedNode = _.cloneDeep(deletedNode) + // Remove the deleted node from backRefsNamesLookup and referencedNodesLookup - backRefsNamesLookup.delete(deletedNode) - referencedNodesLookup.delete(deletedNode) + backRefsNamesLookup.delete(deletedNode.id) + referencedNodesLookup.delete(deletedNode.id) // Remove relationships from other nodes and re-create them. Object.keys(deletedNode.relationships).forEach(key => { let ids = deletedNode.relationships[key] ids = [].concat(ids) ids.forEach(id => { - const node = getNode(id) + let node = getNode(id) // The referenced node might have already been deleted. if (node) { - let referencedNodes = referencedNodesLookup.get(node) + // Clone node so we're not mutating the original node. + node = _.cloneDeep(node) + let referencedNodes = referencedNodesLookup.get(node.id) if (referencedNodes?.includes(deletedNode.id)) { // Loop over relationships and cleanup references. Object.entries(node.relationships).forEach(([key, value]) => { @@ -174,7 +189,7 @@ const handleDeletedNode = async ({ referencedNodes = referencedNodes.filter( nId => nId !== deletedNode.id ) - referencedNodesLookup.set(node, referencedNodes) + referencedNodesLookup.set(node.id, referencedNodes) } // Recreate the referenced node with its now cleaned-up relationships. @@ -237,33 +252,46 @@ ${JSON.stringify(nodeToUpdate, null, 4)} const nodesToUpdate = [newNode] - handleReferences(newNode, { + const oldNodeReferencedNodes = referencedNodesLookup.get(newNode.id) + const backReferencedNodes = handleReferences(newNode, { getNode, + mutateNode: false, createNodeId, entityReferenceRevisions: pluginOptions.entityReferenceRevisions, }) - const oldNode = getNode(newNode.id) + nodesToUpdate.push(...backReferencedNodes) + + let oldNode = getNode(newNode.id) if (oldNode) { + // Clone node so we're not mutating the original node. + oldNode = _.cloneDeep(oldNode) // copy over back references from old node - const backRefsNames = backRefsNamesLookup.get(oldNode) + const backRefsNames = backRefsNamesLookup.get(oldNode.id) if (backRefsNames) { - backRefsNamesLookup.set(newNode, backRefsNames) + backRefsNamesLookup.set(newNode.id, backRefsNames) backRefsNames.forEach(backRefFieldName => { newNode.relationships[backRefFieldName] = oldNode.relationships[backRefFieldName] }) } - const oldNodeReferencedNodes = referencedNodesLookup.get(oldNode) - const newNodeReferencedNodes = referencedNodesLookup.get(newNode) + const newNodeReferencedNodes = referencedNodesLookup.get(newNode.id) // see what nodes are no longer referenced and remove backRefs from them - const removedReferencedNodes = _.difference( + let removedReferencedNodes = _.difference( oldNodeReferencedNodes, newNodeReferencedNodes ).map(id => getNode(id)) + removedReferencedNodes = removedReferencedNodes.map(node => { + if (node) { + return _.cloneDeep(node) + } else { + return node + } + }) + nodesToUpdate.push(...removedReferencedNodes) const nodeFieldName = `${newNode.internal.type}___NODE` @@ -278,23 +306,9 @@ ${JSON.stringify(nodeToUpdate, null, 4)} ) } }) - - // see what nodes are newly referenced, and make sure to call `createNode` on them - const addedReferencedNodes = _.difference( - newNodeReferencedNodes, - oldNodeReferencedNodes - ).map(id => getNode(id)) - - nodesToUpdate.push(...addedReferencedNodes) - } else { - // if we are inserting new node, we need to update all referenced nodes - const newNodes = referencedNodesLookup.get(newNode) - if (typeof newNodes !== `undefined`) { - newNodes.forEach(id => nodesToUpdate.push(getNode(id))) - } } - // download file + // Download file. const { skipFileDownloads } = pluginOptions if (isFileNode(newNode) && !skipFileDownloads) { await downloadFile(