-
Notifications
You must be signed in to change notification settings - Fork 10.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix(gatsby-source-drupal): Clone nodes before adding back references so Gatsby knows the node has changed (so knows to re-run queries) #33328
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. On the initial sourcing, it does make sense to directly mutate nodes as none of them have been created yet. |
||
} 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. | ||
|
@@ -230,33 +245,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` | ||
|
@@ -271,23 +299,9 @@ ${JSON.stringify(nodeToUpdate, null, 4)} | |
) | ||
} | ||
}) | ||
|
||
// see what nodes are newly referenced, and make sure to call `createNode` on them | ||
const addedReferencedNodes = _.difference( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This code isn't needed as it just duplicates what handleReferences does. We now return nodes with updated backreferences from that function so we can eliminate this code. |
||
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( | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
move to regular maps w/ IDs as we're not holding consistent references to the same node object anymore.