Skip to content

Commit 44875b8

Browse files
vladarGatsbyJS Bot
authored andcommitted
fix(gatsby): support union types for one-to-one ___NODE relations (#19916)
1 parent 7e842df commit 44875b8

File tree

4 files changed

+60
-36
lines changed

4 files changed

+60
-36
lines changed

packages/gatsby/src/schema/__tests__/rebuild-schema.js

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -374,8 +374,7 @@ describe(`build and update individual types`, () => {
374374
expect(String(field.type)).toEqual(`[Foo]`)
375375
})
376376

377-
it(`doesn't change ___NODE relations (defined as string)`, async () => {
378-
// FIXME: this behavior seems a bit inconsistent, we should possibly reconsider it
377+
it(`changes ___NODE relations (defined as string) from object type to union and back`, async () => {
379378
const node = {
380379
id: `Bar1`,
381380
internal: { type: `Bar`, contentDigest: `0` },
@@ -392,9 +391,9 @@ describe(`build and update individual types`, () => {
392391
}
393392
newSchema = await addNodeAndRebuild(node2)
394393
field = newSchema.getType(`Bar`).getFields().related
395-
expect(String(field.type)).toEqual(`Foo`)
394+
expect(String(field.type)).toEqual(`FooNestedUnion`)
396395

397-
newSchema = await deleteNodeAndRebuild(node)
396+
newSchema = await deleteNodeAndRebuild(node2)
398397
field = newSchema.getType(`Bar`).getFields().related
399398
expect(String(field.type)).toEqual(`Foo`)
400399
})

packages/gatsby/src/schema/infer/__tests__/inference-metadata.js

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -680,7 +680,10 @@ describe(`Get example value for type inference`, () => {
680680
],
681681
typeConflictReporter,
682682
})
683-
expect(example.related___NODE).toBe(`foo`)
683+
expect(example.related___NODE).toEqual({
684+
multiple: false,
685+
linkedNodes: [`foo`, `bar`, `baz`],
686+
})
684687
})
685688

686689
it(`aggregates array of related node ids`, () => {
@@ -692,7 +695,10 @@ describe(`Get example value for type inference`, () => {
692695
],
693696
typeConflictReporter,
694697
})
695-
expect(example.related___NODE).toEqual([`foo`, `bar`, `baz`])
698+
expect(example.related___NODE).toEqual({
699+
multiple: true,
700+
linkedNodes: [`foo`, `bar`, `baz`],
701+
})
696702
})
697703

698704
it(`skips nullish values and empty arrays/objects`, () => {
@@ -1033,8 +1039,8 @@ describe(`Type change detection`, () => {
10331039
{ object: { foo: `foo`, bar: `bar` } },
10341040
{ list: [`item`], bar: `bar` },
10351041
{ listOfObjects: [{ foo: `foo`, bar: `bar` }] },
1036-
{ union___NODE: `foo` },
1037-
{ listOfUnion___NODE: [`foo`] },
1042+
{ relatedNode___NODE: `foo` },
1043+
{ relatedNodeList___NODE: [`foo`] },
10381044
]
10391045

10401046
const addOne = (node, metadata = initialMetadata) =>
@@ -1125,39 +1131,38 @@ describe(`Type change detection`, () => {
11251131
expect(haveEqualFields(metadata, initialMetadata)).toEqual(true)
11261132
})
11271133

1128-
// TODO
1129-
it.skip(`detects on any change of the union field`, () => {
1134+
it(`detects on any change of the relatedNode field`, () => {
11301135
// We do not know a type of the node being added hence consider and
11311136
// add/delete to such fields as mutations
1132-
let metadata = addOne({ union___NODE: `added` })
1137+
let metadata = addOne({ relatedNode___NODE: `added` })
11331138
expect(metadata.dirty).toEqual(true)
11341139
expect(haveEqualFields(metadata, initialMetadata)).toEqual(true)
11351140
metadata.dirty = false
11361141

1137-
metadata = deleteOne({ union___NODE: `added` }, metadata)
1142+
metadata = deleteOne({ relatedNode___NODE: `added` }, metadata)
11381143
expect(metadata.dirty).toEqual(true)
11391144
expect(haveEqualFields(metadata, initialMetadata)).toEqual(true)
11401145
})
11411146

1142-
it.skip(`does not detect when the same node added to the union field`, () => {
1143-
const metadata = addOne({ union___NODE: `foo` })
1147+
it(`does not detect when the same node added to the relatedNode field`, () => {
1148+
const metadata = addOne({ relatedNode___NODE: `foo` })
11441149
expect(metadata.dirty).toEqual(false)
11451150
expect(haveEqualFields(metadata, initialMetadata)).toEqual(true)
11461151
})
11471152

1148-
it(`detects on any change of the listOfUnion field`, () => {
1149-
let metadata = addOne({ listOfUnion___NODE: [`added`] })
1153+
it(`detects on any change of the relatedNodeList field`, () => {
1154+
let metadata = addOne({ relatedNodeList___NODE: [`added`] })
11501155
expect(metadata.dirty).toEqual(true)
11511156
expect(haveEqualFields(metadata, initialMetadata)).toEqual(true)
11521157
metadata.dirty = false
11531158

1154-
metadata = deleteOne({ listOfUnion___NODE: [`added`] }, metadata)
1159+
metadata = deleteOne({ relatedNodeList___NODE: [`added`] }, metadata)
11551160
expect(metadata.dirty).toEqual(true)
11561161
expect(haveEqualFields(metadata, initialMetadata)).toEqual(true)
11571162
})
11581163

1159-
it(`does not detect when the same node added to the listOfUnion field`, () => {
1160-
const metadata = addOne({ listOfUnion___NODE: [`foo`] })
1164+
it(`does not detect when the same node added to the relatedNodeList field`, () => {
1165+
const metadata = addOne({ relatedNodeList___NODE: [`foo`] })
11611166
expect(metadata.dirty).toEqual(false)
11621167
expect(haveEqualFields(metadata, initialMetadata)).toEqual(true)
11631168
})

packages/gatsby/src/schema/infer/add-inferred-fields.js

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,7 @@ const getFieldConfig = ({
169169
value: exampleValue,
170170
key: unsanitizedKey,
171171
})
172+
arrays = arrays + (value.multiple ? 1 : 0)
172173
} else {
173174
fieldConfig = getSimpleFieldConfig({
174175
schemaComposer,
@@ -255,9 +256,7 @@ const getFieldConfigFromFieldNameConvention = ({
255256
? nodeStore.getNodes().find(node => _.get(node, foreignKey) === value)
256257
: nodeStore.getNode(value)
257258

258-
const linkedNodes = Array.isArray(value)
259-
? value.map(getNodeBy)
260-
: [getNodeBy(value)]
259+
const linkedNodes = value.linkedNodes.map(getNodeBy)
261260

262261
const linkedTypes = _.uniq(
263262
linkedNodes.filter(Boolean).map(node => node.internal.type)
@@ -266,7 +265,7 @@ const getFieldConfigFromFieldNameConvention = ({
266265
invariant(
267266
linkedTypes.length,
268267
`Encountered an error trying to infer a GraphQL type for: \`${key}\`. ` +
269-
`There is no corresponding node with the \`id\` field matching: "${value}".`
268+
`There is no corresponding node with the \`id\` field matching: "${value.linkedNodes}".`
270269
)
271270

272271
let type

packages/gatsby/src/schema/infer/inference-metadata.js

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,8 @@ type ValueDescriptor = {
6969
string?: { total: Count, first: NodeId, example: string, empty: Count },
7070
boolean?: { total: Count, first: NodeId, example: boolean },
7171
array?: { total: Count, first: NodeId, item: ValueDescriptor },
72-
listOfUnion?: { total: Count, first: NodeId, nodes: { [NodeId]: Count } },
72+
relatedNode?: { total: Count, first: NodeId, nodes: { [NodeId]: Count } },
73+
relatedNodeList?: { total: Count, first: NodeId, nodes: { [NodeId]: Count } },
7374
object?: { total: 0, first: NodeId, props: { [string]: ValueDescriptor } },
7475
}
7576
```
@@ -94,6 +95,9 @@ const getType = (value, key) => {
9495
case `number`:
9596
return is32BitInteger(value) ? `int` : `float`
9697
case `string`:
98+
if (key.includes(`___NODE`)) {
99+
return `relatedNode`
100+
}
97101
return looksLikeADate(value) ? `date` : `string`
98102
case `boolean`:
99103
return `boolean`
@@ -105,7 +109,7 @@ const getType = (value, key) => {
105109
if (value.length === 0) {
106110
return `null`
107111
}
108-
return key.includes(`___NODE`) ? `listOfUnion` : `array`
112+
return key.includes(`___NODE`) ? `relatedNodeList` : `array`
109113
}
110114
if (!Object.keys(value).length) return `null`
111115
return `object`
@@ -115,7 +119,7 @@ const getType = (value, key) => {
115119
}
116120

117121
const updateValueDescriptor = (
118-
{ nodeId, key, value, operation = `add` /*: add | del*/, descriptor = {} },
122+
{ nodeId, key, value, operation = `add` /* add | del */, descriptor = {} },
119123
path = []
120124
) => {
121125
// The object may be traversed multiple times from root.
@@ -216,15 +220,20 @@ const _updateValueDescriptor = (
216220
})
217221
break
218222
}
219-
case `listOfUnion`: {
223+
case `relatedNode`:
224+
case `relatedNodeList`: {
220225
const { nodes = {} } = typeInfo
221-
value.forEach(nodeId => {
226+
const listOfNodeIds = Array.isArray(value) ? value : [value]
227+
listOfNodeIds.forEach(nodeId => {
222228
nodes[nodeId] = (nodes[nodeId] || 0) + delta
223229

224230
// Treat any new related node addition or removal as a structural change
225231
// FIXME: this will produce false positives as this node can be
226-
// of the same type as another node already in the map (but we don't know it)
227-
dirty = dirty || nodes[nodeId] === 0 || nodes[nodeId] === 1
232+
// of the same type as another node already in the map (but we don't know it here)
233+
dirty =
234+
dirty ||
235+
nodes[nodeId] === 0 ||
236+
(operation === `add` && nodes[nodeId] === 1)
228237
})
229238
typeInfo.nodes = nodes
230239
break
@@ -284,7 +293,8 @@ const descriptorsAreEqual = (descriptor, otherDescriptor) => {
284293
)
285294
)
286295
}
287-
case `listOfUnion`: {
296+
case `relatedNode`:
297+
case `relatedNodeList`: {
288298
return isEqual(descriptor.nodes, otherDescriptor.nodes)
289299
}
290300
default:
@@ -369,14 +379,21 @@ const resolveWinnerType = descriptor => {
369379

370380
const prepareConflictExamples = (descriptor, isArrayItem) => {
371381
const typeNameMapper = typeName => {
372-
if (typeName === `listOfUnion`) {
382+
if (typeName === `relatedNode`) {
383+
return `string`
384+
}
385+
if (typeName === `relatedNodeList`) {
373386
return `[string]`
374387
}
375388
return [`float`, `int`].includes(typeName) ? `number` : typeName
376389
}
377390
const reportedValueMapper = typeName => {
378-
if (typeName === `listOfUnion`) {
379-
const { nodes } = descriptor.listOfUnion
391+
if (typeName === `relatedNode`) {
392+
const { nodes } = descriptor.relatedNode
393+
return Object.keys(nodes).find(key => nodes[key] > 0)
394+
}
395+
if (typeName === `relatedNodeList`) {
396+
const { nodes } = descriptor.relatedNodeList
380397
return Object.keys(nodes).filter(key => nodes[key] > 0)
381398
}
382399
if (typeName === `object`) {
@@ -458,9 +475,13 @@ const buildExampleValue = ({
458475
return exampleItemValue === null ? null : [exampleItemValue]
459476
}
460477

461-
case `listOfUnion`: {
478+
case `relatedNode`:
479+
case `relatedNodeList`: {
462480
const { nodes = {} } = typeInfo
463-
return Object.keys(nodes).filter(key => nodes[key] > 0)
481+
return {
482+
multiple: type === `relatedNodeList`,
483+
linkedNodes: Object.keys(nodes).filter(key => nodes[key] > 0),
484+
}
464485
}
465486

466487
case `object`: {

0 commit comments

Comments
 (0)