UNPKG

@apollo/federation

Version:
325 lines (299 loc) 8.19 kB
import { GraphQLSchema, specifiedDirectives, DocumentNode, Kind, extendSchema, GraphQLError, } from 'graphql'; import { validateSDL } from 'graphql/validation/validate'; import gql from 'graphql-tag'; import { typeSerializer } from 'apollo-federation-integration-testsuite'; import { buildMapsFromServiceList } from '../../../compose'; import { knownSubgraphDirectives } from '@apollo/subgraph/dist/directives'; import { UniqueFieldDefinitionNames } from '..'; import { ServiceDefinition } from '../../../types'; expect.addSnapshotSerializer(typeSerializer); // simulate the first half of the composition process function createDocumentsForServices( serviceList: ServiceDefinition[], ): DocumentNode[] { const { typeDefinitionsMap, typeExtensionsMap } = buildMapsFromServiceList( serviceList, ); return [ { kind: Kind.DOCUMENT, definitions: Object.values(typeDefinitionsMap).flat(), }, { kind: Kind.DOCUMENT, definitions: Object.values(typeExtensionsMap).flat(), }, ]; } describe('UniqueFieldDefinitionNames', () => { let schema: GraphQLSchema; // create a blank schema for each test beforeEach(() => { schema = new GraphQLSchema({ query: undefined, directives: [...specifiedDirectives, ...knownSubgraphDirectives], }); }); describe('enforces unique field names for', () => { it('object type definitions', () => { const [definitions, extensions] = createDocumentsForServices([ { typeDefs: gql` type Product { sku: ID! } `, name: 'serviceA', }, { typeDefs: gql` extend type Product { sku: Int! } `, name: 'serviceB', }, ]); const errors = validateSDL(definitions, schema, [ UniqueFieldDefinitionNames, ]) as GraphQLError[]; schema = extendSchema(schema, definitions, { assumeValidSDL: true, }); errors.push( ...validateSDL(extensions, schema, [UniqueFieldDefinitionNames]), ); expect(errors).toHaveLength(1); expect(errors[0].message).toMatch( 'Field "Product.sku" already exists in the schema.', ); }); it('interface definitions', () => { const [definitions, extensions] = createDocumentsForServices([ { typeDefs: gql` interface Product { sku: ID! } `, name: 'serviceA', }, { typeDefs: gql` extend interface Product { sku: String! } `, name: 'serviceB', }, ]); const errors = validateSDL(definitions, schema, [ UniqueFieldDefinitionNames, ]) as GraphQLError[]; schema = extendSchema(schema, definitions, { assumeValidSDL: true }); errors.push( ...validateSDL(extensions, schema, [UniqueFieldDefinitionNames]), ); expect(errors).toHaveLength(1); expect(errors[0].message).toMatch( 'Field "Product.sku" already exists in the schema.', ); }); it('input object definitions', () => { const [definitions, extensions] = createDocumentsForServices([ { typeDefs: gql` input Product { sku: ID } `, name: 'serviceA', }, { typeDefs: gql` extend input Product { sku: String! } `, name: 'serviceB', }, ]); const errors = validateSDL(definitions, schema, [ UniqueFieldDefinitionNames, ]) as GraphQLError[]; schema = extendSchema(schema, definitions, { assumeValidSDL: true }); errors.push( ...validateSDL(extensions, schema, [UniqueFieldDefinitionNames]), ); expect(errors).toHaveLength(1); expect(errors[0].message).toMatch( 'Field "Product.sku" already exists in the schema.', ); }); }); describe('permits duplicate field names for', () => { it('value types (identical object types)', () => { const [definitions] = createDocumentsForServices([ { typeDefs: gql` type Product { sku: ID! color: String } `, name: 'serviceA', }, { typeDefs: gql` type Product { sku: ID! color: String } `, name: 'serviceB', }, ]); const errors = validateSDL(definitions, schema, [ UniqueFieldDefinitionNames, ]); expect(errors).toHaveLength(0); }); it('value types (identical interface types)', () => { const [definitions] = createDocumentsForServices([ { typeDefs: gql` interface Product { sku: ID! color: String } `, name: 'serviceA', }, { typeDefs: gql` interface Product { sku: ID! color: String } `, name: 'serviceB', }, ]); const errors = validateSDL(definitions, schema, [ UniqueFieldDefinitionNames, ]); expect(errors).toHaveLength(0); }); it('value types (identical input types)', () => { const [definitions] = createDocumentsForServices([ { typeDefs: gql` input Product { sku: ID! color: String } `, name: 'serviceA', }, { typeDefs: gql` input Product { sku: ID! color: String } `, name: 'serviceB', }, ]); const errors = validateSDL(definitions, schema, [ UniqueFieldDefinitionNames, ]); expect(errors).toHaveLength(0); }); it('object type definitions (non-identical, value types with type mismatch)', () => { const [definitions] = createDocumentsForServices([ { typeDefs: gql` type Product { sku: ID! color: String quantity: Int } `, name: 'serviceA', }, { typeDefs: gql` type Product { sku: String! color: String quantity: Int! } `, name: 'serviceB', }, ]); const errors = validateSDL(definitions, schema, [ UniqueFieldDefinitionNames, ]); expect(errors).toHaveLength(0); }); it('object type definitions field and arguments with different type', () => { const typeDefs = gql` type Campaign implements Node { identifier: String event(identifier: String!): Event } `; const [definitions] = createDocumentsForServices([ { typeDefs, name: 'serviceA', }, { typeDefs, name: 'serviceB', }, ]); const errors = validateSDL(definitions, schema, [ UniqueFieldDefinitionNames, ]); expect(errors).toHaveLength(0); }); }); it('object type definitions order does not impact diffing', () => { const [definitions] = createDocumentsForServices([ { typeDefs: gql` type Order implements Node { questions(answers: OrderAnswersInput): Questions fees(answers: OrderAnswersInput): Fees answers: [Answer] } `, name: 'serviceA', }, { typeDefs: gql` type Order implements Node { questions(answers: OrderAnswersInput): Questions answers: [Answer] fees(answers: OrderAnswersInput): Fees } `, name: 'serviceB', }, ]); const errors = validateSDL(definitions, schema, [ UniqueFieldDefinitionNames, ]); expect(errors).toHaveLength(0); }); });