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
Can we merge schemas with overriding types in fields? #287
Comments
/* @flow */
import { buildSchema, GraphQLSchema } from 'graphql';
import { SchemaComposer, dedent } from '../..';
describe('github issue #: Object directives are removed from schema', () => {
it('should keep @test directive on TestObject', () => {
const schemaA = buildSchema(`
type Query {
field1: Int
"""KEEP ME"""
field2: Int
}
`);
const schemaB = buildSchema(`
type Query {
"""BBB"""
field1: String
field3: String
field22: Int
}
`);
const sc = new SchemaComposer(schemaA);
sc.merge(schemaB);
expect(sc.toSDL({ omitScalars: true, omitDirectiveDefinitions: true })).toEqual(dedent`
type Mutation
type Query {
"""BBB"""
field1: String
"""KEEP ME"""
field2: Int
field3: String
field22: Int
}
type Subscription
`);
expect(sc.buildSchema()).toBeInstanceOf(GraphQLSchema);
});
}); |
Code: const schemaA = buildSchema(`
# An object with an ID
interface Node {
# The id of the object.
id: ID!
}
type Post implements Node {
id: ID!
content: String
fieldA: Int
fieldB: String
}
type Query {
post: Post
# Fetches an object given its ID
node(
# The ID of an object
id: ID!
): Node
}
`);
const schemaB = buildSchema(`
# An object with an ID
interface Node {
# The id of the object.
id: ID!
}
type Post implements Node {
id: ID!
content: String
}
type Query {
post: Post
# Fetches an object given its ID
node(
# The ID of an object
id: ID!
): Node
}
`);
const sc = new SchemaComposer(schemaA);
sc.merge(schemaB);
const schemaC = sc.toSDL();
console.log('schemaC', schemaC); Expected result: Type The `ID` scalar type represents a unique identifier, often used to refetch an object or as key for a cache. The ID type appears in a JSON response as a String; however, it is not intended to be human-readable. When expected as an input type, any string (such as `"4"`) or integer (such as `4`) input value will be accepted as an ID.
"""
scalar ID
type Mutation
interface Node {
id: ID!
}
type Post implements Node {
id: ID!
content: String
fieldA: Int
fieldB: String
}
type Query {
post: Post
node(id: ID!): Node
}
"""
The `String` scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text.
"""
scalar String
type Subscription Actual result: Type The `ID` scalar type represents a unique identifier, often used to refetch an object or as key for a cache. The ID type appears in a JSON response as a String; however, it is not intended to be human-readable. When expected as an input type, any string (such as `"4"`) or integer (such as `4`) input value will be accepted as an ID.
"""
scalar ID
type Mutation
interface Node {
id: ID!
}
type Post implements Node {
id: ID!
content: String
}
type Query {
post: Post
node(id: ID!): Node
}
"""
The `String` scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text.
"""
scalar String
type Subscription |
Moreover, if I remove |
If type Mutation
type Query
type Subscription I expected the following: The `ID` scalar type represents a unique identifier, often used to refetch an object or as key for a cache. The ID type appears in a JSON response as a String; however, it is not intended to be human-readable. When expected as an input type, any string (such as `"4"`) or integer (such as `4`) input value will be accepted as an ID.
"""
scalar ID
type Mutation
interface Node {
id: ID!
}
type Post implements Node {
id: ID!
content: String
fieldA: Int
fieldB: String
}
"""
The `String` scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text.
"""
scalar String
type Query
type Subscription |
const typeDefsA = `
type AggregatePost {
count: Int!
}
type BatchPayload {
"""The number of nodes that have been affected by the Batch operation."""
count: Long!
}
"""
The Long scalar type represents non-fractional signed whole numeric values.
Long can represent values between -(2^63) and 2^63 - 1.
"""
scalar Long
type Mutation {
createPost(data: PostCreateInput!): Post!
updatePost(data: PostUpdateInput!, where: PostWhereUniqueInput!): Post
deletePost(where: PostWhereUniqueInput!): Post
upsertPost(where: PostWhereUniqueInput!, create: PostCreateInput!, update: PostUpdateInput!): Post!
updateManyPosts(data: PostUpdateManyMutationInput!, where: PostWhereInput): BatchPayload!
deleteManyPosts(where: PostWhereInput): BatchPayload!
}
enum MutationType {
CREATED
UPDATED
DELETED
}
"""An object with an ID"""
interface Node {
"""The id of the object."""
id: ID!
}
"""Information about pagination in a connection."""
type PageInfo {
"""When paginating forwards, are there more items?"""
hasNextPage: Boolean!
"""When paginating backwards, are there more items?"""
hasPreviousPage: Boolean!
"""When paginating backwards, the cursor to continue."""
startCursor: String
"""When paginating forwards, the cursor to continue."""
endCursor: String
}
type Post implements Node {
id: ID!
title: String!
content: String!
published: Boolean!
}
"""A connection to a list of items."""
type PostConnection {
"""Information to aid in pagination."""
pageInfo: PageInfo!
"""A list of edges."""
edges: [PostEdge]!
aggregate: AggregatePost!
}
input PostCreateInput {
id: ID
title: String!
content: String!
published: Boolean
}
"""An edge in a connection."""
type PostEdge {
"""The item at the end of the edge."""
node: Post!
"""A cursor for use in pagination."""
cursor: String!
}
enum PostOrderByInput {
id_ASC
id_DESC
title_ASC
title_DESC
content_ASC
content_DESC
published_ASC
published_DESC
updatedAt_ASC
updatedAt_DESC
createdAt_ASC
createdAt_DESC
}
type PostPreviousValues {
id: ID!
title: String!
content: String!
published: Boolean!
}
type PostSubscriptionPayload {
mutation: MutationType!
node: Post
updatedFields: [String!]
previousValues: PostPreviousValues
}
input PostSubscriptionWhereInput {
"""Logical AND on all given filters."""
AND: [PostSubscriptionWhereInput!]
"""Logical OR on all given filters."""
OR: [PostSubscriptionWhereInput!]
"""Logical NOT on all given filters combined by AND."""
NOT: [PostSubscriptionWhereInput!]
"""The subscription event gets dispatched when it's listed in mutation_in"""
mutation_in: [MutationType!]
"""
The subscription event gets only dispatched when one of the updated fields names is included in this list
"""
updatedFields_contains: String
"""
The subscription event gets only dispatched when all of the field names included in this list have been updated
"""
updatedFields_contains_every: [String!]
"""
The subscription event gets only dispatched when some of the field names included in this list have been updated
"""
updatedFields_contains_some: [String!]
node: PostWhereInput
}
input PostUpdateInput {
title: String
content: String
published: Boolean
}
input PostUpdateManyMutationInput {
title: String
content: String
published: Boolean
}
input PostWhereInput {
"""Logical AND on all given filters."""
AND: [PostWhereInput!]
"""Logical OR on all given filters."""
OR: [PostWhereInput!]
"""Logical NOT on all given filters combined by AND."""
NOT: [PostWhereInput!]
id: ID
"""All values that are not equal to given value."""
id_not: ID
"""All values that are contained in given list."""
id_in: [ID!]
"""All values that are not contained in given list."""
id_not_in: [ID!]
"""All values less than the given value."""
id_lt: ID
"""All values less than or equal the given value."""
id_lte: ID
"""All values greater than the given value."""
id_gt: ID
"""All values greater than or equal the given value."""
id_gte: ID
"""All values containing the given string."""
id_contains: ID
"""All values not containing the given string."""
id_not_contains: ID
"""All values starting with the given string."""
id_starts_with: ID
"""All values not starting with the given string."""
id_not_starts_with: ID
"""All values ending with the given string."""
id_ends_with: ID
"""All values not ending with the given string."""
id_not_ends_with: ID
title: String
"""All values that are not equal to given value."""
title_not: String
"""All values that are contained in given list."""
title_in: [String!]
"""All values that are not contained in given list."""
title_not_in: [String!]
"""All values less than the given value."""
title_lt: String
"""All values less than or equal the given value."""
title_lte: String
"""All values greater than the given value."""
title_gt: String
"""All values greater than or equal the given value."""
title_gte: String
"""All values containing the given string."""
title_contains: String
"""All values not containing the given string."""
title_not_contains: String
"""All values starting with the given string."""
title_starts_with: String
"""All values not starting with the given string."""
title_not_starts_with: String
"""All values ending with the given string."""
title_ends_with: String
"""All values not ending with the given string."""
title_not_ends_with: String
content: String
"""All values that are not equal to given value."""
content_not: String
"""All values that are contained in given list."""
content_in: [String!]
"""All values that are not contained in given list."""
content_not_in: [String!]
"""All values less than the given value."""
content_lt: String
"""All values less than or equal the given value."""
content_lte: String
"""All values greater than the given value."""
content_gt: String
"""All values greater than or equal the given value."""
content_gte: String
"""All values containing the given string."""
content_contains: String
"""All values not containing the given string."""
content_not_contains: String
"""All values starting with the given string."""
content_starts_with: String
"""All values not starting with the given string."""
content_not_starts_with: String
"""All values ending with the given string."""
content_ends_with: String
"""All values not ending with the given string."""
content_not_ends_with: String
published: Boolean
"""All values that are not equal to given value."""
published_not: Boolean
}
input PostWhereUniqueInput {
id: ID
}
type Query {
posts(where: PostWhereInput, orderBy: PostOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): [Post]!
post(where: PostWhereUniqueInput!): Post
postsConnection(where: PostWhereInput, orderBy: PostOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): PostConnection!
"""Fetches an object given its ID"""
node(
"""The ID of an object"""
id: ID!
): Node
}
type Subscription {
post(where: PostSubscriptionWhereInput): PostSubscriptionPayload
}
`;
const typeDefsB = `
type AggregatePost {
count: Int!
}
type BatchPayload {
# The number of nodes that have been affected by the Batch operation.
count: Long!
}
# The Long scalar type represents non-fractional signed whole numeric values.
# Long can represent values between -(2^63) and 2^63 - 1.
scalar Long
type Mutation {
createPost(data: PostCreateInput!): Post!
updatePost(data: PostUpdateInput!, where: PostWhereUniqueInput!): Post
deletePost(where: PostWhereUniqueInput!): Post
upsertPost(
where: PostWhereUniqueInput!
create: PostCreateInput!
update: PostUpdateInput!
): Post!
updateManyPosts(
data: PostUpdateManyMutationInput!
where: PostWhereInput
): BatchPayload!
deleteManyPosts(where: PostWhereInput): BatchPayload!
}
enum MutationType {
CREATED
UPDATED
DELETED
}
# An object with an ID
interface Node {
# The id of the object.
id: ID!
}
# Information about pagination in a connection.
type PageInfo {
# When paginating forwards, are there more items?
hasNextPage: Boolean!
# When paginating backwards, are there more items?
hasPreviousPage: Boolean!
# When paginating backwards, the cursor to continue.
startCursor: String
# When paginating forwards, the cursor to continue.
endCursor: String
}
type Post implements Node {
id: ID!
content: String
ololo: String
azaza: Int
}
# A connection to a list of items.
type PostConnection {
# Information to aid in pagination.
pageInfo: PageInfo!
# A list of edges.
edges: [PostEdge]!
aggregate: AggregatePost!
}
input PostCreateInput {
id: ID
title: String!
azaza: String
content: String!
published: Boolean
}
# An edge in a connection.
type PostEdge {
# The item at the end of the edge.
node: Post!
# A cursor for use in pagination.
cursor: String!
}
enum PostOrderByInput {
id_ASC
id_DESC
title_ASC
title_DESC
content_ASC
content_DESC
published_ASC
published_DESC
updatedAt_ASC
updatedAt_DESC
createdAt_ASC
createdAt_DESC
}
type PostPreviousValues {
id: ID!
title: String!
content: String!
published: Boolean!
}
type PostSubscriptionPayload {
mutation: MutationType!
node: Post
updatedFields: [String!]
previousValues: PostPreviousValues
}
input PostSubscriptionWhereInput {
# Logical AND on all given filters.
AND: [PostSubscriptionWhereInput!]
# Logical OR on all given filters.
OR: [PostSubscriptionWhereInput!]
# Logical NOT on all given filters combined by AND.
NOT: [PostSubscriptionWhereInput!]
# The subscription event gets dispatched when it's listed in mutation_in
mutation_in: [MutationType!]
# The subscription event gets only dispatched when one of the updated fields names is included in this list
updatedFields_contains: String
# The subscription event gets only dispatched when all of the field names included in this list have been updated
updatedFields_contains_every: [String!]
# The subscription event gets only dispatched when some of the field names included in this list have been updated
updatedFields_contains_some: [String!]
node: PostWhereInput
}
input PostUpdateInput {
title: String
content: String
published: Boolean
}
input PostUpdateManyMutationInput {
title: String
content: String
published: Boolean
}
input PostWhereInput {
# Logical AND on all given filters.
AND: [PostWhereInput!]
# Logical OR on all given filters.
OR: [PostWhereInput!]
# Logical NOT on all given filters combined by AND.
NOT: [PostWhereInput!]
id: ID
# All values that are not equal to given value.
id_not: ID
# All values that are contained in given list.
id_in: [ID!]
# All values that are not contained in given list.
id_not_in: [ID!]
# All values less than the given value.
id_lt: ID
# All values less than or equal the given value.
id_lte: ID
# All values greater than the given value.
id_gt: ID
# All values greater than or equal the given value.
id_gte: ID
# All values containing the given string.
id_contains: ID
# All values not containing the given string.
id_not_contains: ID
# All values starting with the given string.
id_starts_with: ID
# All values not starting with the given string.
id_not_starts_with: ID
# All values ending with the given string.
id_ends_with: ID
# All values not ending with the given string.
id_not_ends_with: ID
title: String
# All values that are not equal to given value.
title_not: String
# All values that are contained in given list.
title_in: [String!]
# All values that are not contained in given list.
title_not_in: [String!]
# All values less than the given value.
title_lt: String
# All values less than or equal the given value.
title_lte: String
# All values greater than the given value.
title_gt: String
# All values greater than or equal the given value.
title_gte: String
# All values containing the given string.
title_contains: String
# All values not containing the given string.
title_not_contains: String
# All values starting with the given string.
title_starts_with: String
# All values not starting with the given string.
title_not_starts_with: String
# All values ending with the given string.
title_ends_with: String
# All values not ending with the given string.
title_not_ends_with: String
content: String
# All values that are not equal to given value.
content_not: String
# All values that are contained in given list.
content_in: [String!]
# All values that are not contained in given list.
content_not_in: [String!]
# All values less than the given value.
content_lt: String
# All values less than or equal the given value.
content_lte: String
# All values greater than the given value.
content_gt: String
# All values greater than or equal the given value.
content_gte: String
# All values containing the given string.
content_contains: String
# All values not containing the given string.
content_not_contains: String
# All values starting with the given string.
content_starts_with: String
# All values not starting with the given string.
content_not_starts_with: String
# All values ending with the given string.
content_ends_with: String
# All values not ending with the given string.
content_not_ends_with: String
published: Boolean
# All values that are not equal to given value.
published_not: Boolean
azaza: String
}
input PostWhereUniqueInput {
id: ID
}
type Query {
posts(
where: PostWhereInput
orderBy: PostOrderByInput
skip: Int
after: String
before: String
first: Int
last: Int
): [Post]!
post(where: PostWhereUniqueInput!): Post
postsConnection(
where: PostWhereInput
orderBy: PostOrderByInput
skip: Int
after: String
before: String
first: Int
last: Int
): PostConnection!
# Fetches an object given its ID
node(
# The ID of an object
id: ID!
): Node
}
type Subscription {
post(where: PostSubscriptionWhereInput): PostSubscriptionPayload
}
`;
const schemaA = buildSchema(typeDefsA);
const schemaB = buildSchema(typeDefsB);
const sc = new SchemaComposer(schemaA);
sc.merge(schemaB);
const schemaC = sc.buildSchema(); produces the following error:
|
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@FluorescentHallucinogen described his needs in ardatan/graphql-tools#2051
The text was updated successfully, but these errors were encountered: