Skip to content

Commit

Permalink
Don't infer types for plugin provided GQL types
Browse files Browse the repository at this point in the history
  • Loading branch information
jquense committed May 21, 2018
1 parent 678e5a1 commit fc18251
Show file tree
Hide file tree
Showing 11 changed files with 301 additions and 169 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@
"yargs": "^10.0.3"
},
"engines": {
"yarn": "^1.2.1"
"yarn": "^1.2.1",
"node": ">=6.11.5"
},
"eslintIgnore": ["interfaces", "**/__tests__/fixtures/"],
"private": true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ Object {
"anArray": Object {
"field": "anArray",
},
"context___nestedObject___name": Object {
"field": "context___nestedObject___name",
},
"context___nestedObject___someOtherProperty": Object {
"field": "context___nestedObject___someOtherProperty",
},
Expand Down Expand Up @@ -51,6 +54,7 @@ Object {
],
"context": Object {
"nestedObject": Object {
"name": "Inner name",
"someOtherProperty": 1,
},
},
Expand Down
93 changes: 64 additions & 29 deletions packages/gatsby/src/schema/__tests__/data-tree-utils-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,67 +89,77 @@ describe(`Gatsby data tree utils`, () => {
},
context: {
nestedObject: {
name: `Inner name`,
someOtherProperty: 3,
},
},
},
]

it(`builds field examples from an array of nodes`, () => {
expect(getExampleValues(nodes)).toMatchSnapshot()
expect(getExampleValues({ nodes })).toMatchSnapshot()
})

it(`skips null fields`, () => {
expect(getExampleValues(nodes).iAmNull).not.toBeDefined()
expect(getExampleValues({ nodes }).iAmNull).not.toBeDefined()
})

it(`should not mutate the nodes`, () => {
getExampleValues(nodes)
getExampleValues({ nodes })
expect(nodes[0].context.nestedObject).toBeNull()
expect(nodes[1].context.nestedObject.someOtherProperty).toEqual(1)
expect(nodes[2].context.nestedObject.someOtherProperty).toEqual(2)
expect(nodes[3].context.nestedObject.someOtherProperty).toEqual(3)
})

it(`skips empty or sparse arrays`, () => {
expect(getExampleValues(nodes).emptyArray).not.toBeDefined()
expect(getExampleValues(nodes).hair).toBeDefined()
expect(getExampleValues({ nodes }).emptyArray).not.toBeDefined()
expect(getExampleValues({ nodes }).hair).toBeDefined()
})

it(`skips ignoredFields at the top level`, () => {
const example = getExampleValues({
nodes,
ignoreFields: [`name`, `anArray`],
})

expect(example.name).not.toBeDefined()
expect(example.anArray).not.toBeDefined()
expect(example.hair).toBeDefined()
expect(example.context.nestedObject.name).toBeDefined()
})

it(`build enum values for fields from array on nodes`, () => {
expect(buildFieldEnumValues(nodes)).toMatchSnapshot()
expect(buildFieldEnumValues({ nodes })).toMatchSnapshot()
})

it(`turns polymorphic fields null`, () => {
let example = getExampleValues([
{ foo: null },
{ foo: [1] },
{ foo: { field: 1 } },
])
let example = getExampleValues({
nodes: [{ foo: null }, { foo: [1] }, { foo: { field: 1 } }],
})
expect(example.foo).toBe(INVALID_VALUE)
})

it(`handles polymorphic arrays`, () => {
let example = getExampleValues([
{ foo: [[`foo`, `bar`]] },
{ foo: [{ field: 1 }] },
])
let example = getExampleValues({
nodes: [{ foo: [[`foo`, `bar`]] }, { foo: [{ field: 1 }] }],
})
expect(example.foo).toBe(INVALID_VALUE)
})

it(`doesn't confuse empty fields for polymorhpic ones`, () => {
let example = getExampleValues([
{ foo: { bar: 1 } },
{ foo: null },
{ foo: { field: 1 } },
])
let example = getExampleValues({
nodes: [{ foo: { bar: 1 } }, { foo: null }, { foo: { field: 1 } }],
})
expect(example.foo).toEqual({ field: 1, bar: 1 })

example = getExampleValues([
{ foo: [{ bar: 1 }] },
{ foo: null },
{ foo: [{ field: 1 }, { baz: 1 }] },
])
example = getExampleValues({
nodes: [
{ foo: [{ bar: 1 }] },
{ foo: null },
{ foo: [{ field: 1 }, { baz: 1 }] },
],
})
expect(example.foo).toEqual([{ field: 1, bar: 1, baz: 1 }])
})

Expand Down Expand Up @@ -199,7 +209,7 @@ describe(`Type conflicts`, () => {
},
]

getExampleValues({ nodes, type: `NoConflict` })
getExampleValues({ nodes, typeName: `NoConflict` })

expect(addConflictExampleSpy).not.toBeCalled()
})
Expand All @@ -222,15 +232,14 @@ describe(`Type conflicts`, () => {
},
]

getExampleValues({ nodes, type: `Conflict_1` })
getExampleValues({ nodes, typeName: `Conflict_1` })

expect(addConflictSpy).toBeCalled()
expect(addConflictSpy).toBeCalledWith(
`Conflict_1.stringOrNumber`,
expect.any(Array)
)

// expect(addConflictExampleSpy).toBeCalled()
expect(addConflictExampleSpy).toHaveBeenCalledTimes(2)
expect(addConflictExampleSpy).toBeCalledWith(
expect.objectContaining({
Expand All @@ -256,7 +265,7 @@ describe(`Type conflicts`, () => {
},
]

getExampleValues({ nodes, type: `Conflict_2` })
getExampleValues({ nodes, typeName: `Conflict_2` })
expect(addConflictSpy).toBeCalled()
expect(addConflictSpy).toBeCalledWith(
`Conflict_2.arrayOfMixedType`,
Expand All @@ -273,4 +282,30 @@ describe(`Type conflicts`, () => {
})
)
})

it(`Doesn't report ignored fields`, () => {
const nodes = [
{
id: `id1`,
stringOrNumber: `string`,
other: 1,
},
{
id: `id2`,
stringOrNumber: 5,
other: `foo`,
},
]

getExampleValues({
nodes,
typeName: `Conflict_3`,
ignoreFields: [`stringOrNumber`],
})

expect(addConflictSpy).toBeCalled()
expect(addConflictSpy).toBeCalledWith(`Conflict_3.other`, expect.any(Array))
expect(addConflictSpy).not.toBeCalledWith(`Conflict_3.stringOrNumber`)
expect(addConflictExampleSpy).toBeCalled()
})
})
45 changes: 44 additions & 1 deletion packages/gatsby/src/schema/__tests__/infer-graphql-type-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ const {
const path = require(`path`)
const normalizePath = require(`normalize-path`)
const { clearTypeExampleValues } = require(`../data-tree-utils`)
const { typeConflictReporter } = require(`../type-conflict-reporter`)
const { inferObjectStructureFromNodes } = require(`../infer-graphql-type`)

function queryResult(nodes, fragment, { types = [] } = {}) {
function queryResult(nodes, fragment, { types = [], ignoreFields } = {}) {
const schema = new GraphQLSchema({
query: new GraphQLObjectType({
name: `RootQueryType`,
Expand All @@ -22,6 +23,7 @@ function queryResult(nodes, fragment, { types = [] } = {}) {
name: `Test`,
fields: inferObjectStructureFromNodes({
nodes,
ignoreFields,
types: [{ name: `Test` }, ...types],
}),
})
Expand Down Expand Up @@ -725,4 +727,45 @@ describe(`GraphQL type inferance`, () => {
}
`
).then(result => expect(result).toMatchSnapshot()))

describe(`type conflicts`, () => {
let addConflictSpy = jest.spyOn(typeConflictReporter, `addConflict`)

beforeEach(() => {
addConflictSpy.mockReset()
})

it(`catches conflicts and removes field`, async () => {
let result = await queryResult(
[{ foo: `foo`, number: 1.1 }, { foo: `bar`, number: `1` }],
`
foo
number
`
)
expect(addConflictSpy).toHaveBeenCalledTimes(1)

expect(result.errors.length).toEqual(1)
expect(result.errors[0].message).toMatch(
`Cannot query field "number" on type "Test".`
)
})

it(`does not warn about provided types`, async () => {
let result = await queryResult(
[{ foo: `foo`, number: 1.1 }, { foo: `bar`, number: `1` }],
`
foo
number
`,
{ ignoreFields: [`number`] }
)
expect(addConflictSpy).not.toHaveBeenCalled()

expect(result.errors.length).toEqual(1)
expect(result.errors[0].message).toMatch(
`Cannot query field "number" on type "Test".`
)
})
})
})
2 changes: 1 addition & 1 deletion packages/gatsby/src/schema/build-connection-fields.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const { buildFieldEnumValues } = require(`./data-tree-utils`)
module.exports = type => {
const enumValues = buildFieldEnumValues({
nodes: type.nodes,
type: type.name,
typeName: type.name,
})
const { connectionType: groupConnection } = connectionDefinitions({
name: _.camelCase(`${type.name} groupConnection`),
Expand Down
7 changes: 2 additions & 5 deletions packages/gatsby/src/schema/build-node-types.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,7 @@ const { nodeInterface } = require(`./node-interface`)
const { getNodes, getNode, getNodeAndSavePathDependency } = require(`../redux`)
const { createPageDependency } = require(`../redux/actions/add-page-dependency`)
const { setFileNodeRootType } = require(`./types/type-file`)
const {
clearTypeExampleValues,
getExampleValues,
} = require(`./data-tree-utils`)
const { clearTypeExampleValues } = require(`./data-tree-utils`)

import type { ProcessedNodeType } from "./infer-graphql-type"

Expand Down Expand Up @@ -118,7 +115,7 @@ module.exports = async () => {
const inferredFields = inferObjectStructureFromNodes({
nodes: type.nodes,
types: _.values(processedTypes),
exampleValue: getExampleValues({ type: type.name, nodes: type.nodes }),
ignoreFields: Object.keys(type.fieldsFromPlugins),
})

return {
Expand Down
Loading

0 comments on commit fc18251

Please sign in to comment.