graphql-tools: Schema extensions with interfaces fails in v7
Description
As you can see in the tests suite, schema extensions with interfaces fails in v7 when we use complexe fragments. I wrote this regression test “Schema extensions with interfaces -> should resolve with 2 fragments” to show you a failure example. I also added you some successful examples with simple queries with interfaces, complexe queries without interface and type merging. I hope this will help you 😉
Maybe related to https://github.com/ardatan/graphql-tools/issues/2157?
Tests suite
import { batchDelegateToSchema } from '@graphql-tools/batch-delegate'
import { makeExecutableSchema } from '@graphql-tools/schema'
import { stitchSchemas } from '@graphql-tools/stitch'
import { graphql } from 'graphql'
const expectedData = [
{
network: { id: '57', domains: [{ id: '60', name: 'network57.com' }] },
},
]
describe('Schema extensions with interfaces', () => {
const networkSchema = makeExecutableSchema({
typeDefs: `
interface Domain {
id: ID!
name: String!
}
type Domain1 implements Domain {
id: ID!
name: String!
}
type Domain2 implements Domain {
id: ID!
name: String!
extra: String!
}
type Network {
id: ID!
domains: [Domain!]!
}
type Query {
networks(ids: [ID!]!): [Network!]!
}
`,
resolvers: {
Domain: {
__resolveType() {
return 'Domain1'
},
},
Query: {
networks: (root, { ids }) =>
ids.map((id) => ({ id, domains: [{ id: Number(id) + 3, name: `network${id}.com` }] })),
},
},
})
const postsSchema = makeExecutableSchema({
typeDefs: `
type Network {
id: ID!
}
type Post {
id: ID!
network: Network!
}
type Query {
posts(ids: [ID!]!): [Post]!
}
`,
resolvers: {
Query: {
posts: (root, { ids }) =>
ids.map((id) => ({
id,
network: { id: Number(id) + 2 },
})),
},
},
})
const gatewaySchema = stitchSchemas({
subschemas: [{ schema: networkSchema }, { schema: postsSchema }],
resolvers: {
Post: {
network: {
selectionSet: '{ network { id } }',
resolve(parent, _args, context, info) {
return batchDelegateToSchema({
key: parent.network.id,
argsFromKeys: (ids) => ({ ids }),
context,
fieldName: 'networks',
info,
operation: 'query',
schema: networkSchema,
})
},
},
},
},
})
it('should resolve with no fragments', async () => {
const { data } = await graphql(
gatewaySchema,
`
query {
posts(ids: [55]) {
network {
id
domains {
id
name
}
}
}
}
`,
)
expect(data.posts).toEqual(expectedData)
})
it('should resolve with 1 fragment', async () => {
const { data } = await graphql(
gatewaySchema,
`
query {
posts(ids: [55]) {
network {
domains {
name
}
}
...F2
}
}
fragment F2 on Post {
network {
id
domains {
id
}
}
}
`,
)
expect(data.posts).toEqual(expectedData)
})
// Fails with `Cannot return null for non-nullable field Domain1.id` because batchDelegateToSchema returns
// { domains: [{ name: 'network57.com', __typename: 'Domain1' }], id: '57' }
it('should resolve with 2 fragments', async () => {
const { data } = await graphql(
gatewaySchema,
`
query {
posts(ids: [55]) {
network {
domains {
name
}
}
...F2
}
}
fragment F2 on Post {
network {
...F3
}
}
fragment F3 on Network {
id
domains {
id
}
}
`,
)
expect(data.posts).toEqual(expectedData)
})
})
describe('Schema extensions with no interfaces', () => {
const networkSchema = makeExecutableSchema({
typeDefs: `
type Domain {
id: ID!
name: String!
}
type Network {
id: ID!
domains: [Domain!]!
}
type Query {
networks(ids: [ID!]!): [Network!]!
}
`,
resolvers: {
Query: {
networks: (root, { ids }) =>
ids.map((id) => ({ id, domains: [{ id: Number(id) + 3, name: `network${id}.com` }] })),
},
},
})
const postsSchema = makeExecutableSchema({
typeDefs: `
type Network {
id: ID!
}
type Post {
id: ID!
network: Network!
}
type Query {
posts(ids: [ID!]!): [Post]!
}
`,
resolvers: {
Query: {
posts: (root, { ids }) =>
ids.map((id) => ({
id,
network: { id: Number(id) + 2 },
})),
},
},
})
const gatewaySchema = stitchSchemas({
subschemas: [{ schema: networkSchema }, { schema: postsSchema }],
resolvers: {
Post: {
network: {
selectionSet: '{ network { id } }',
resolve(parent, _args, context, info) {
return batchDelegateToSchema({
key: parent.network.id,
argsFromKeys: (ids) => ({ ids }),
context,
fieldName: 'networks',
info,
operation: 'query',
schema: networkSchema,
})
},
},
},
},
})
it('should resolve with 2 fragments', async () => {
const { data } = await graphql(
gatewaySchema,
`
query {
posts(ids: [55]) {
network {
domains {
name
}
}
...F2
}
}
fragment F2 on Post {
network {
...F3
}
}
fragment F3 on Network {
id
domains {
id
}
}
`,
)
expect(data.posts).toEqual(expectedData)
})
})
describe('Type merging with interfaces', () => {
const networkSchema = makeExecutableSchema({
typeDefs: `
interface Domain {
id: ID!
name: String!
}
type Domain1 implements Domain {
id: ID!
name: String!
}
type Domain2 implements Domain {
id: ID!
name: String!
extra: String!
}
type Network {
id: ID!
domains: [Domain!]!
}
type Query {
networks(ids: [ID!]!): [Network!]!
}
`,
resolvers: {
Domain: {
__resolveType() {
return 'Domain1'
},
},
Query: {
networks: (root, { ids }) =>
ids.map((id) => ({ id, domains: [{ id: Number(id) + 3, name: `network${id}.com` }] })),
},
},
})
const postsSchema = makeExecutableSchema({
typeDefs: `
type Network {
id: ID!
}
type Post {
id: ID!
network: Network!
}
type Query {
posts(ids: [ID!]!): [Post]!
}
`,
resolvers: {
Query: {
posts: (root, { ids }) =>
ids.map((id) => ({
id,
network: { id: Number(id) + 2 },
})),
},
},
})
const gatewaySchema = stitchSchemas({
subschemas: [
{
schema: networkSchema,
merge: {
Network: {
selectionSet: '{ id }',
fieldName: 'networks',
key: ({ id }) => id,
argsFromKeys: (ids) => ({ ids }),
},
},
},
{
schema: postsSchema,
},
],
})
it('should resolve with 2 fragments', async () => {
const { data } = await graphql(
gatewaySchema,
`
query {
posts(ids: [55]) {
network {
domains {
name
}
}
...F2
}
}
fragment F2 on Post {
network {
...F3
}
}
fragment F3 on Network {
id
domains {
id
}
}
`,
)
expect(data.posts).toEqual(expectedData)
})
})
About this issue
- Original URL
- State: closed
- Created 4 years ago
- Comments: 15 (2 by maintainers)
Commits related to this issue
- add tests from #2173 — committed to ardatan/graphql-tools by yaacovCR 4 years ago
- fix(delegate): WrapConcreteTypes regression WrapConcreteTypes should not process fragments that are not on a root type. addresses #2173 — committed to ardatan/graphql-tools by yaacovCR 4 years ago
- fix(delegate): WrapConcreteTypes regression (#2174) WrapConcreteTypes should not process fragments that are not on a root type. addresses #2173 — committed to ardatan/graphql-tools by yaacovCR 4 years ago
Not sure if that’s the cause per se will have time to dig in hopefully later this week