Skip to content

Commit c720767

Browse files
KyleAMathewswardpeetgatsbybot
authored
fix(gatsby-source-drupal): delete relationships to now deleted nodes (#32971)
* fix(gatsby-source-drupal): delete relationships to now deleted nodes * Cleanup back/reference weakmaps * Fix lint error * Update packages/gatsby-source-drupal/src/utils.js Co-authored-by: Ward Peeters <[email protected]> * Actually recreate updated nodes * referenced nodes doesn't always exist Co-authored-by: Ward Peeters <[email protected]> Co-authored-by: gatsbybot <[email protected]>
1 parent 2b7296b commit c720767

File tree

5 files changed

+149
-29
lines changed

5 files changed

+149
-29
lines changed

packages/gatsby-source-drupal/src/__tests__/fixtures/1593545806.json

+5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
{
22
"timestamp": 1593545807,
33
"entities": [
4+
{
5+
"action": "delete",
6+
"id": "file-4",
7+
"type": "file--file"
8+
},
49
{
510
"jsonapi": {
611
"version": "1.0",

packages/gatsby-source-drupal/src/__tests__/fixtures/article.json

+26
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,32 @@
4343
"id": "file-1"
4444
}
4545
},
46+
"field_secondary_image": {
47+
"data": [
48+
{
49+
"type": "file--file",
50+
"id": "file-4"
51+
}
52+
]
53+
},
54+
"field_secondary_multiple_image": {
55+
"data": [
56+
{
57+
"type": "file--file",
58+
"id": "file-3"
59+
},
60+
{
61+
"type": "file--file",
62+
"id": "file-4"
63+
}
64+
]
65+
},
66+
"field_tertiary_image": {
67+
"data": {
68+
"type": "file--file",
69+
"id": "file-4"
70+
}
71+
},
4672
"field_tags": {
4773
"data": null
4874
}

packages/gatsby-source-drupal/src/__tests__/index.js

+23
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ describe(`gatsby-source-drupal`, () => {
3232
const { objectContaining } = expect
3333
const actions = {
3434
createNode: jest.fn(node => (nodes[node.id] = node)),
35+
deleteNode: jest.fn(node => delete nodes[node.id]),
3536
setPluginStatus: jest.fn(),
3637
touchNode: jest.fn(),
3738
}
@@ -433,15 +434,22 @@ describe(`gatsby-source-drupal`, () => {
433434
const fastBuilds = true
434435
await sourceNodes(args, { baseUrl, fastBuilds })
435436
})
437+
436438
it(`Attributes`, () => {
437439
expect(nodes[createNodeId(`und.article-3`)].title).toBe(`Article #3`)
438440
})
441+
439442
it(`Relationships`, () => {
440443
expect(nodes[createNodeId(`und.article-3`)].relationships).toEqual({
441444
field_main_image___NODE: createNodeId(`und.file-1`),
442445
field_tags___NODE: [createNodeId(`und.tag-1`)],
443446
})
447+
expect(
448+
nodes[createNodeId(`und.article-2`)].relationships
449+
.field_secondary_image___NODE
450+
).toEqual([createNodeId(`und.file-4`)])
444451
})
452+
445453
it(`Back references`, () => {
446454
expect(
447455
nodes[createNodeId(`und.file-1`)].relationships[
@@ -473,17 +481,32 @@ describe(`gatsby-source-drupal`, () => {
473481
})
474482
await sourceNodes(args, { baseUrl, fastBuilds })
475483
})
484+
476485
it(`Attributes`, () => {
477486
expect(nodes[createNodeId(`und.article-3`)].title).toBe(
478487
`Article #3 - Synced`
479488
)
480489
})
490+
481491
it(`Relationships`, () => {
482492
// removed `field_main_image`, changed `field_tags`
483493
expect(nodes[createNodeId(`und.article-3`)].relationships).toEqual({
484494
field_tags___NODE: [createNodeId(`und.tag-2`)],
485495
})
496+
expect(
497+
nodes[createNodeId(`und.article-2`)].relationships
498+
.field_secondary_image___NODE
499+
).toBe(undefined)
500+
expect(
501+
nodes[createNodeId(`und.article-2`)].relationships
502+
.field_secondary_multiple_image___NODE.length
503+
).toBe(1)
504+
expect(
505+
nodes[createNodeId(`und.article-2`)].relationships
506+
.field_tertiary_image___NODE_image___NODE
507+
).toBe(undefined)
486508
})
509+
487510
it(`Back references`, () => {
488511
// removed `field_main_image`, `file-1` no longer has back reference to `article-3`
489512
expect(

packages/gatsby-source-drupal/src/gatsby-node.js

+22-29
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,11 @@ const {
1414
isFileNode,
1515
createNodeIdWithVersion,
1616
} = require(`./normalize`)
17-
const { handleReferences, handleWebhookUpdate } = require(`./utils`)
17+
const {
18+
handleReferences,
19+
handleWebhookUpdate,
20+
handleDeletedNode,
21+
} = require(`./utils`)
1822

1923
const agent = {
2024
http: new HttpAgent(),
@@ -149,19 +153,15 @@ exports.sourceNodes = async (
149153
}
150154

151155
for (const nodeToDelete of nodesToDelete) {
152-
const nodeIdToDelete = createNodeId(
153-
createNodeIdWithVersion(
154-
nodeToDelete.id,
155-
nodeToDelete.type,
156-
getOptions().languageConfig
157-
? nodeToDelete.attributes?.langcode
158-
: `und`,
159-
nodeToDelete.attributes?.drupal_internal__revision_id,
160-
entityReferenceRevisions
161-
)
162-
)
163-
actions.deleteNode(getNode(nodeIdToDelete))
164-
reporter.log(`Deleted node: ${nodeIdToDelete}`)
156+
const deletedNode = await handleDeletedNode({
157+
actions,
158+
getNode,
159+
node: nodeToDelete,
160+
createNodeId,
161+
createContentDigest,
162+
entityReferenceRevisions,
163+
})
164+
reporter.log(`Deleted node: ${deletedNode.id}`)
165165
}
166166

167167
changesActivity.end()
@@ -248,21 +248,14 @@ exports.sourceNodes = async (
248248
const nodesToSync = res.body.entities
249249
for (const nodeSyncData of nodesToSync) {
250250
if (nodeSyncData.action === `delete`) {
251-
actions.deleteNode(
252-
getNode(
253-
createNodeId(
254-
createNodeIdWithVersion(
255-
nodeSyncData.id,
256-
nodeSyncData.type,
257-
getOptions().languageConfig
258-
? nodeSyncData.attributes.langcode
259-
: `und`,
260-
nodeSyncData.attributes?.drupal_internal__revision_id,
261-
entityReferenceRevisions
262-
)
263-
)
264-
)
265-
)
251+
handleDeletedNode({
252+
actions,
253+
getNode,
254+
node: nodeSyncData,
255+
createNodeId,
256+
createContentDigest,
257+
entityReferenceRevisions,
258+
})
266259
} else {
267260
// The data could be a single Drupal entity or an array of Drupal
268261
// entities to update.

packages/gatsby-source-drupal/src/utils.js

+73
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,78 @@ const handleReferences = (
110110

111111
exports.handleReferences = handleReferences
112112

113+
const handleDeletedNode = async ({
114+
actions,
115+
node,
116+
getNode,
117+
createNodeId,
118+
createContentDigest,
119+
entityReferenceRevisions,
120+
}) => {
121+
const deletedNode = getNode(
122+
createNodeId(
123+
createNodeIdWithVersion(
124+
node.id,
125+
node.type,
126+
getOptions().languageConfig ? node.attributes.langcode : `und`,
127+
node.attributes?.drupal_internal__revision_id,
128+
entityReferenceRevisions
129+
)
130+
)
131+
)
132+
133+
// Remove the deleted node from backRefsNamesLookup
134+
backRefsNamesLookup.delete(deletedNode)
135+
136+
// Remove relationships from other nodes and re-create them.
137+
Object.keys(deletedNode.relationships).forEach(key => {
138+
let ids = deletedNode.relationships[key]
139+
ids = [].concat(ids)
140+
ids.forEach(id => {
141+
const node = getNode(id)
142+
let referencedNodes = referencedNodesLookup.get(node)
143+
if (referencedNodes?.includes(deletedNode.id)) {
144+
// Loop over relationships and cleanup references.
145+
Object.entries(node.relationships).forEach(([key, value]) => {
146+
// If a string ref matches, delete it.
147+
if (_.isString(value) && value === deletedNode.id) {
148+
delete node.relationships[key]
149+
}
150+
151+
// If it's an array, filter, then check if the array is empty and then delete
152+
// if so
153+
if (_.isArray(value)) {
154+
value = value.filter(v => v !== deletedNode.id)
155+
156+
if (value.length === 0) {
157+
delete node.relationships[key]
158+
} else {
159+
node.relationships[key] = value
160+
}
161+
}
162+
})
163+
164+
// Remove deleted node from array of referencedNodes
165+
referencedNodes = referencedNodes.filter(nId => nId !== deletedNode.id)
166+
referencedNodesLookup.set(node, referencedNodes)
167+
}
168+
// Recreate the referenced node with its now cleaned-up relationships.
169+
if (node.internal.owner) {
170+
delete node.internal.owner
171+
}
172+
if (node.fields) {
173+
delete node.fields
174+
}
175+
node.internal.contentDigest = createContentDigest(node)
176+
actions.createNode(node)
177+
})
178+
})
179+
180+
actions.deleteNode(deletedNode)
181+
182+
return deletedNode
183+
}
184+
113185
const handleWebhookUpdate = async (
114186
{
115187
nodeToUpdate,
@@ -216,3 +288,4 @@ const handleWebhookUpdate = async (
216288
}
217289

218290
exports.handleWebhookUpdate = handleWebhookUpdate
291+
exports.handleDeletedNode = handleDeletedNode

0 commit comments

Comments
 (0)