Skip to content
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): always add both childField and childrenField in GraphQL #28656

Merged
merged 9 commits into from
Dec 17, 2020
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 1 addition & 15 deletions docs/docs/reference/graphql-data-layer/schema-customization.md
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ The types passed in are used to determine child relations of the node.

#### Defining child relations

The `@childOf` extension can be used to explicitly define what node types or media types a node is a child of and immediately add `child[MyType]` or `children[MyType]` as a field on the parent.
The `@childOf` extension can be used to explicitly define what node types or media types a node is a child of and immediately add `child[MyType]` and `children[MyType]` fields on the parent.

The `types` argument takes an array of strings and determines what node types the node is a child of:

Expand Down Expand Up @@ -255,20 +255,6 @@ type Mdx implements Node
}
```

If `many: true` is set, then instead of creating a single child field on the parent, it will create multiple:

```graphql
# Adds `childMdx1` with type `Mdx1` to `File`.
type Mdx1 implements Node @childOf(types: ["File"]) {
id: ID!
}

# Adds `childrenMdx2` with type `[Mdx2]` as a field of `File`.
type Mdx2 implements Node @childOf(types: ["File"], many: true) {
id: ID!
}
```

#### Nested types

So far, the example project has only been dealing with scalar values (`String` and `Date`;
Expand Down
2 changes: 1 addition & 1 deletion docs/docs/schema-generation.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ This allows adding GraphQL Fields to any node type. This operates on GraphQL typ

## 4. Parent / children relationships

Nodes can be connected into _child-parent_ relationships either by using [`createParentChildLink`](/docs/reference/config-files/actions/#createParentChildLink) or by adding the `parent` field to raw node data. Child types can always access parent with the `parent` field in GraphQL. Parent types also get `children` fields as well as "convenience child fields" `child[TypeName]` or `children[TypeName]`.
Nodes can be connected into _child-parent_ relationships either by using [`createParentChildLink`](/docs/reference/config-files/actions/#createParentChildLink) or by adding the `parent` field to raw node data. Child types can always access parent with the `parent` field in GraphQL. Parent types also get `children` fields as well as "convenience child fields" `child[TypeName]` and `children[TypeName]`.

Children types are either inferred from data or created using `@childOf` directive, either by parent type name or by `mimeType` (only for File parent types).

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ directive @mimeTypes(
) on OBJECT

\\"\\"\\"
Define parent-child relations between types. This is used to add \`child*\` or
Define parent-child relations between types. This is used to add \`child*\` and
\`children*\` convenience fields like \`childImageSharp\`.
\\"\\"\\"
directive @childOf(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ Object {
"childImageSharp": Object {
"id": "0b06bdfe-f253-5859-8321-b42b5d737195",
},
"childrenImageSharp": Array [
Object {
"id": "0b06bdfe-f253-5859-8321-b42b5d737195",
},
],
},
"time": "06 kesäkuu",
},
Expand All @@ -64,6 +69,11 @@ Object {
"childImageSharp": Object {
"id": "f5f0e564-899f-5bbc-92de-a2325b69cb75",
},
"childrenImageSharp": Array [
Object {
"id": "f5f0e564-899f-5bbc-92de-a2325b69cb75",
},
],
},
"likes": 8,
"localeFormat": "05 huhtikuu 2017",
Expand All @@ -83,6 +93,11 @@ Object {
"childImageSharp": Object {
"id": "6ba97198-5331-53d6-a0e0-c9c063c5094e",
},
"childrenImageSharp": Array [
Object {
"id": "6ba97198-5331-53d6-a0e0-c9c063c5094e",
},
],
},
"likes": 9,
"localeFormat": "11 syyskuu 2017",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ directive @mimeTypes(
) on OBJECT

\\"\\"\\"
Define parent-child relations between types. This is used to add \`child*\` or
Define parent-child relations between types. This is used to add \`child*\` and
\`children*\` convenience fields like \`childImageSharp\`.
\\"\\"\\"
directive @childOf(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,9 @@ describe(`build-node-connections`, () => {
childRelative { # lol
id
}
childrenRelative {
id
}
}
}
}
Expand All @@ -193,6 +196,9 @@ describe(`build-node-connections`, () => {

expect(allParent.edges[0].node.childRelative).toBeDefined()
expect(allParent.edges[0].node.childRelative.id).toEqual(`r1`)

expect(allParent.edges[0].node.childrenRelative).toBeDefined()
expect(allParent.edges[0].node.childrenRelative).toEqual([{ id: `r1` }])
})

it(`should create page dependency`, async () => {
Expand Down
8 changes: 7 additions & 1 deletion packages/gatsby/src/schema/__tests__/build-node-types.js
Original file line number Diff line number Diff line change
Expand Up @@ -154,19 +154,25 @@ describe(`build-node-types`, () => {
})

it(`should create typed children fields`, async () => {
let { parent } = await runQuery(
const { parent } = await runQuery(
`
{
parent(id: { eq: "p1" }) {
childrenChild { # lol
id
}
childChild {
id
}
}
}
`
)
expect(parent.childrenChild).toBeDefined()
expect(parent.childrenChild.map(c => c.id)).toEqual([`c1`, `c2`])

expect(parent.childChild).toBeDefined()
expect(parent.childChild).toEqual({ id: `c1` })
})

it(`should create typed child field for singular children`, async () => {
Expand Down
6 changes: 6 additions & 0 deletions packages/gatsby/src/schema/__tests__/kitchen-sink.js
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,9 @@ describe(`Kitchen sink schema test`, () => {
childImageSharp {
id
}
childrenImageSharp {
id
}
}
}
}
Expand All @@ -146,6 +149,9 @@ describe(`Kitchen sink schema test`, () => {
childImageSharp {
id
}
childrenImageSharp {
id
}
}
_3invalidKey
}
Expand Down
12 changes: 12 additions & 0 deletions packages/gatsby/src/schema/__tests__/queries.js
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ describe(`Query schema`, () => {
edges {
node {
childMarkdown { frontmatter { title } }
childrenMarkdown { frontmatter { title } }
}
}
}
Expand All @@ -236,16 +237,23 @@ describe(`Query schema`, () => {
{
node: {
childMarkdown: { frontmatter: { title: `Markdown File 1` } },
childrenMarkdown: [
{ frontmatter: { title: `Markdown File 1` } },
],
},
},
{
node: {
childMarkdown: { frontmatter: { title: `Markdown File 2` } },
childrenMarkdown: [
{ frontmatter: { title: `Markdown File 2` } },
],
},
},
{
node: {
childMarkdown: null,
childrenMarkdown: [],
},
},
],
Expand All @@ -261,6 +269,7 @@ describe(`Query schema`, () => {
allFile {
edges {
node {
childAuthor { name }
childrenAuthor { name }
}
}
Expand All @@ -273,16 +282,19 @@ describe(`Query schema`, () => {
edges: [
{
node: {
childAuthor: null,
childrenAuthor: [],
},
},
{
node: {
childAuthor: null,
childrenAuthor: [],
},
},
{
node: {
childAuthor: { name: `Author 2` },
childrenAuthor: [{ name: `Author 2` }, { name: `Author 1` }],
},
},
Expand Down
9 changes: 7 additions & 2 deletions packages/gatsby/src/schema/__tests__/rebuild-schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -541,7 +541,9 @@ describe(`build and update individual types`, () => {
const fields = newSchema.getType(`Foo`).getFields()
const fieldNames = Object.keys(fields).sort()
expect(fieldNames).toEqual(
initialFooFields.concat(`childBar`, `childBaz`).sort()
initialFooFields
.concat(`childBar`, `childBaz`, `childrenBar`, `childrenBaz`)
.sort()
)
expect(String(fields.childBar.type)).toEqual(`Bar`)
expect(String(fields.childBaz.type)).toEqual(`Baz`)
Expand Down Expand Up @@ -573,7 +575,10 @@ describe(`build and update individual types`, () => {

const fields = newSchema.getType(`Foo`).getFields()
const fieldNames = Object.keys(fields).sort()
expect(fieldNames).toEqual(initialFooFields.concat(`childrenBar`).sort())
expect(fieldNames).toEqual(
initialFooFields.concat(`childBar`, `childrenBar`).sort()
)
expect(String(fields.childBar.type)).toEqual(`Bar`)
expect(String(fields.childrenBar.type)).toEqual(`[Bar]`)

await expectSymmetricDelete(nodes)
Expand Down
50 changes: 29 additions & 21 deletions packages/gatsby/src/schema/extensions/__tests__/child-relations.js
Original file line number Diff line number Diff line change
Expand Up @@ -167,15 +167,16 @@ describe(`Define parent-child relationships with field extensions`, () => {
type Child implements Node {
id: ID!
}
type AnotherChild implements Node @childOf(types: ["Parent"], many: true) {
type AnotherChild implements Node @childOf(types: ["Parent"]) {
id: ID!
}
`)
)
await buildSchema()
expect(report.warn).toBeCalledTimes(1)
expect(report.warn.mock.calls[0][0]).toEqual(
`Deprecation warning: In Gatsby v3 field \`Parent.childChild\` will not be added automatically ` +
`Deprecation warning: In Gatsby v3 fields \`Parent.childChild\` and \`Parent.childrenChild\` ` +
`will not be added automatically ` +
`because type \`Child\` does not explicitly list type \`Parent\` in \`childOf\` extension.\n` +
`Add the following type definition to fix this:\n\n` +
` type Child implements Node @childOf(types: ["Parent"]) {\n` +
Expand All @@ -185,19 +186,19 @@ describe(`Define parent-child relationships with field extensions`, () => {
)
})

it(`adds child fields to parent type with childOf(many: true) extension`, async () => {
it(`adds children[Field] field to parent type with childOf extension`, async () => {
dispatch(
createTypes(`
type Parent implements Node @dontInfer {
id: ID!
}
type Child implements Node @childOf(types: ["Parent"], many: true) {
type Child implements Node @childOf(types: ["Parent"]) {
id: ID!
}
type AnotherChild implements Node @childOf(types: ["Parent"], many: true) {
type AnotherChild implements Node @childOf(types: ["Parent"]) {
id: ID!
}
type ChildWithoutNodes implements Node @childOf(types: ["Parent"], many: true) {
type ChildWithoutNodes implements Node @childOf(types: ["Parent"]) {
id: ID!
}
`)
Expand All @@ -209,10 +210,12 @@ describe(`Define parent-child relationships with field extensions`, () => {
// expect(parentFields.childChild).toBeUndefined() // Deprecated, see above
expect(parentFields.childrenAnotherChild).toBeDefined()
expect(parentFields.childrenAnotherChild.resolve).toBeDefined()
expect(parentFields.childAnotherChild).toBeUndefined()
expect(parentFields.childAnotherChild).toBeDefined()
expect(parentFields.childAnotherChild.resolve).toBeDefined()
expect(parentFields.childrenChildWithoutNodes).toBeDefined()
expect(parentFields.childrenChildWithoutNodes.resolve).toBeDefined()
expect(parentFields.childChildWithoutNodes).toBeUndefined()
expect(parentFields.childChildWithoutNodes).toBeDefined()
expect(parentFields.childChildWithoutNodes.resolve).toBeDefined()
})

it(`shows error when childOf extension is used on type that does not implement the Node interface`, async () => {
Expand All @@ -239,7 +242,7 @@ describe(`Define parent-child relationships with field extensions`, () => {
type Child implements Node @childOf(types: ["Parent"]) {
name: String
}
type AnotherChild implements Node @childOf(types: ["Parent"], many: true) {
type AnotherChild implements Node @childOf(types: ["Parent"]) {
name: String
}
type ChildWithoutNodes implements Node @childOf(types: ["Parent"]) {
Expand Down Expand Up @@ -330,7 +333,6 @@ describe(`Define parent-child relationships with field extensions`, () => {
extensions: {
childOf: {
types: [`Parent`],
many: true,
},
},
}),
Expand Down Expand Up @@ -411,7 +413,7 @@ describe(`Define parent-child relationships with field extensions`, () => {
type Child implements Node @childOf(mimeTypes: ["application/listenup", "multipart/related"]) {
id: ID!
}
type AnotherChild implements Node @childOf(mimeTypes: ["application/listenup", "multipart/related"], many: true) {
type AnotherChild implements Node @childOf(mimeTypes: ["application/listenup", "multipart/related"]) {
id: ID!
}
type ChildWithoutNodes implements Node @childOf(mimeTypes: ["application/listenup", "multipart/related"]) {
Expand All @@ -423,23 +425,29 @@ describe(`Define parent-child relationships with field extensions`, () => {
const parentFields = schema.getType(`Parent`).getFields()
expect(parentFields.childChild).toBeDefined()
expect(parentFields.childChild.resolve).toBeDefined()
expect(parentFields.childrenChild).toBeUndefined()
expect(parentFields.childrenChild).toBeDefined()
expect(parentFields.childrenChild.resolve).toBeDefined()
expect(parentFields.childrenAnotherChild).toBeDefined()
expect(parentFields.childrenAnotherChild.resolve).toBeDefined()
expect(parentFields.childAnotherChild).toBeUndefined()
expect(parentFields.childAnotherChild).toBeDefined()
expect(parentFields.childAnotherChild.resolve).toBeDefined()
expect(parentFields.childChildWithoutNodes).toBeDefined()
expect(parentFields.childChildWithoutNodes.resolve).toBeDefined()
expect(parentFields.childChildrenWithoutNodes).toBeUndefined()
expect(parentFields.childrenChildWithoutNodes).toBeDefined()
expect(parentFields.childrenChildWithoutNodes.resolve).toBeDefined()
const relativeFields = schema.getType(`Relative`).getFields()
expect(relativeFields.childChild).toBeDefined()
expect(relativeFields.childChild.resolve).toBeDefined()
expect(relativeFields.childrenChild).toBeUndefined()
expect(relativeFields.childrenChild).toBeDefined()
expect(relativeFields.childrenChild.resolve).toBeDefined()
expect(relativeFields.childrenAnotherChild).toBeDefined()
expect(relativeFields.childrenAnotherChild.resolve).toBeDefined()
expect(relativeFields.childAnotherChild).toBeUndefined()
expect(relativeFields.childAnotherChild).toBeDefined()
expect(relativeFields.childAnotherChild.resolve).toBeDefined()
expect(relativeFields.childChildWithoutNodes).toBeDefined()
expect(relativeFields.childChildWithoutNodes.resolve).toBeDefined()
expect(relativeFields.childChildrenWithoutNodes).toBeUndefined()
expect(relativeFields.childrenChildWithoutNodes).toBeDefined()
expect(relativeFields.childrenChildWithoutNodes.resolve).toBeDefined()
})

it(`returns correct query results for mime-types`, async () => {
Expand All @@ -454,7 +462,7 @@ describe(`Define parent-child relationships with field extensions`, () => {
type Child implements Node @childOf(mimeTypes: ["application/listenup", "multipart/related"]) {
name: String
}
type AnotherChild implements Node @childOf(mimeTypes: ["application/listenup", "multipart/related"], many: true) {
type AnotherChild implements Node @childOf(mimeTypes: ["application/listenup", "multipart/related"]) {
name: String
}
type ChildWithoutNodes implements Node @childOf(mimeTypes: ["application/listenup", "multipart/related"]) {
Expand Down Expand Up @@ -533,7 +541,7 @@ describe(`Define parent-child relationships with field extensions`, () => {
type Child implements Node @childOf(types: ["Relative"], mimeTypes: ["application/listenup"]) {
name: String
}
type AnotherChild implements Node @childOf(types: ["Relative"], mimeTypes: ["multipart/related"], many: true) {
type AnotherChild implements Node @childOf(types: ["Relative"], mimeTypes: ["multipart/related"]) {
name: String
}
`)
Expand Down Expand Up @@ -598,7 +606,7 @@ describe(`Define parent-child relationships with field extensions`, () => {
type Relative implements Node @dontInfer @mimeTypes(types: ["multipart/related"]) {
id: ID!
}
interface NextGeneration @nodeInterface @childOf(mimeTypes: ["application/listenup", "multipart/related"], many: true) {
interface NextGeneration @nodeInterface @childOf(mimeTypes: ["application/listenup", "multipart/related"]) {
id: ID!
name: String
}
Expand Down Expand Up @@ -688,7 +696,7 @@ describe(`Define parent-child relationships with field extensions`, () => {
type Parent implements Node & Ancestors {
id: ID!
}
type AnotherChild implements Node @childOf(types: ["Ancestors"], many: true) {
type AnotherChild implements Node @childOf(types: ["Ancestors"]) {
name: String
}
`)
Expand Down
Loading