Skip to content

Commit

Permalink
An array of linked nodes linking to heterogeneous node types is now c…
Browse files Browse the repository at this point in the history
…onverted to a union type
  • Loading branch information
KyleAMathews committed Jun 20, 2017
1 parent 3b45684 commit 49052d7
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 22 deletions.
41 changes: 41 additions & 0 deletions packages/gatsby/src/schema/__tests__/infer-graphql-type-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,16 @@ describe(`GraphQL type inferance`, () => {
}),
}),
},
{
name: `Pet`,
nodeObjectType: new GraphQLObjectType({
name: `Pet`,
fields: inferObjectStructureFromNodes({
nodes: [{ id: `pet_1`, species: `dog` }],
types: [{ name: `Pet` }],
}),
}),
},
]

store.dispatch({
Expand All @@ -200,6 +210,10 @@ describe(`GraphQL type inferance`, () => {
type: `CREATE_NODE`,
payload: { id: `child_2`, internal: { type: `Child` }, hair: `blonde` },
})
store.dispatch({
type: `CREATE_NODE`,
payload: { id: `pet_1`, internal: { type: `Pet` }, species: `dog` },
})
})

it(`Links nodes`, async () => {
Expand Down Expand Up @@ -261,6 +275,33 @@ describe(`GraphQL type inferance`, () => {
`"Bar" available to link to this node.`
)
})

it(`Creates union types when an array field is linking to multiple node types`, async () => {
let result = await queryResult(
[{ linked___NODE: [`child_1`, `pet_1`] }],
`
linked {
__typename
... on Child {
hair
}
... on Pet {
species
}
}
`,
{ types }
)
expect(result.errors).not.toBeDefined()
expect(result.data.listNode[0].linked[0].hair).toEqual(`brown`)
expect(result.data.listNode[0].linked[0].__typename).toEqual(`Child`)
expect(result.data.listNode[0].linked[1].species).toEqual(`dog`)
expect(result.data.listNode[0].linked[1].__typename).toEqual(`Pet`)
store.dispatch({
type: `CREATE_NODE`,
payload: { id: `baz`, internal: { type: `Bar` } },
})
})
})

it(`Infers graphql type from array of nodes`, () =>
Expand Down
6 changes: 6 additions & 0 deletions packages/gatsby/src/schema/data-tree-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,12 @@ const extractFieldExamples = (nodes: any[]) =>
if (!array.length) return null
if (!areAllSameType(array)) return INVALID_VALUE

// Linked node arrays don't get reduced further as we
// want to preserve all the linked node types.
if (_.includes(key, `___NODE`)) {
return array
}

// primitive values don't get merged further, just take the first item
if (!_.isObject(array[0])) return array.slice(0, 1)
let merged = extractFieldExamples(array)
Expand Down
75 changes: 53 additions & 22 deletions packages/gatsby/src/schema/infer-graphql-type.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const {
GraphQLFloat,
GraphQLInt,
GraphQLList,
GraphQLUnionType,
} = require(`graphql`)
const _ = require(`lodash`)
const invariant = require(`invariant`)
Expand Down Expand Up @@ -246,37 +247,63 @@ function findLinkedNode(value, linkedField, path) {
function inferFromFieldName(value, selector, types): GraphQLFieldConfig<*, *> {
let isArray = false
if (_.isArray(value)) {
value = value[0]
isArray = true
// Reduce values to nodes with unique types.
value = _.uniqBy(value, v => getNode(v).internal.type)
}

const key = selector.split(`.`).pop()
const [, , linkedField] = key.split(`___`)

const linkedNode = findLinkedNode(value, linkedField)
const validateLinkedNode = linkedNode => {
invariant(
linkedNode,
oneLine`
Encountered an error trying to infer a GraphQL type for: "${selector}".
There is no corresponding node with the ${linkedField || `id`}
field matching: "${value}"
`
)
}
const validateField = (linkedNode, field) => {
invariant(
field,
oneLine`
Encountered an error trying to infer a GraphQL type for: "${selector}".
There is no corresponding GraphQL type "${linkedNode.internal
.type}" available
to link to this node.
`
)
}

invariant(
linkedNode,
oneLine`
Encountered an error trying to infer a GraphQL type for: "${selector}".
There is no corresponding node with the ${linkedField || `id`}
field matching: "${value}"
`
)
const field = types.find(type => type.name === linkedNode.internal.type)

invariant(
field,
oneLine`
Encountered an error trying to infer a GraphQL type for: "${selector}".
There is no corresponding GraphQL type "${linkedNode.internal
.type}" available
to link to this node.
`
)
const findNodeType = node =>
types.find(type => type.name === node.internal.type)

if (isArray) {
const linkedNodes = value.map(v => findLinkedNode(v))
linkedNodes.forEach(node => validateLinkedNode(node))
const fields = linkedNodes.map(node => findNodeType(node))
fields.forEach((field, i) => validateField(linkedNodes[i], field))

let type
// If there's more than one type, we'll create a union type.
if (fields.length > 1) {
type = new GraphQLUnionType({
name: `Union_${key}_${fields.map(f => f.name).join(`__`)}`,
description: `Union interface for the field "${key}" for types [${fields
.map(f => f.name)
.join(`, `)}]`,
types: fields.map(f => f.nodeObjectType),
resolveType: data =>
fields.find(f => f.name == data.internal.type).nodeObjectType,
})
} else {
type = fields[0].nodeObjectType
}

return {
type: new GraphQLList(field.nodeObjectType),
type: new GraphQLList(type),
resolve: (node, a, b = {}) => {
let fieldValue = node[key]
if (fieldValue) {
Expand All @@ -290,6 +317,10 @@ function inferFromFieldName(value, selector, types): GraphQLFieldConfig<*, *> {
}
}

const linkedNode = findLinkedNode(value, linkedField)
validateLinkedNode(linkedNode)
const field = findNodeType(linkedNode)
validateField(linkedNode, field)
return {
type: field.nodeObjectType,
resolve: (node, a, b = {}) => {
Expand Down
1 change: 1 addition & 0 deletions packages/gatsby/src/schema/run-sift.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,5 +77,6 @@ module.exports = ({ args, nodes, connection = false, path = `` }) => {
path,
nodeId: result[0].id,
})

return result[0]
}

0 comments on commit 49052d7

Please sign in to comment.