Skip to content

Commit

Permalink
Separate handling of union validations
Browse files Browse the repository at this point in the history
  • Loading branch information
trevor-scheer committed Jul 23, 2019
1 parent 561470f commit 1f00664
Show file tree
Hide file tree
Showing 6 changed files with 200 additions and 106 deletions.
6 changes: 4 additions & 2 deletions packages/apollo-federation/src/composition/rules.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { specifiedSDLRules } from 'graphql/validation/specifiedRules';

import {
UniqueTypeNamesWithoutEnumsOrScalars,
UniqueTypeNamesWithFields,
MatchingEnums,
PossibleTypeExtensions,
UniqueFieldDefinitionNames,
UniqueUnionTypes,
} from './validate/sdl';

const omit = [
Expand All @@ -19,7 +20,8 @@ export const compositionRules = specifiedSDLRules
.filter(rule => !omit.includes(rule.name))
.concat([
UniqueFieldDefinitionNames,
UniqueTypeNamesWithoutEnumsOrScalars,
UniqueTypeNamesWithFields,
MatchingEnums,
UniqueUnionTypes,
PossibleTypeExtensions,
]);
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import {
GraphQLSchema,
specifiedDirectives,
Kind,
DocumentNode,
} from 'graphql';
import { validateSDL } from 'graphql/validation/validate';
import gql from 'graphql-tag';
import {
typeSerializer,
graphqlErrorSerializer,
} from '../../../../snapshotSerializers';
import { UniqueUnionTypes } from '..';
import { ServiceDefinition } from '../../../types';
import { buildMapsFromServiceList } from '../../../compose';
import federationDirectives from '../../../../directives';

expect.addSnapshotSerializer(graphqlErrorSerializer);
expect.addSnapshotSerializer(typeSerializer);

function createDocumentsForServices(
serviceList: ServiceDefinition[],
): DocumentNode[] {
const { definitionsMap, extensionsMap } = buildMapsFromServiceList(
serviceList,
);
return [
{
kind: Kind.DOCUMENT,
definitions: Object.values(definitionsMap).flat(),
},
{
kind: Kind.DOCUMENT,
definitions: Object.values(extensionsMap).flat(),
},
];
}

describe('MatchingUnions', () => {
let schema: GraphQLSchema;

// create a blank schema for each test
beforeEach(() => {
schema = new GraphQLSchema({
query: undefined,
directives: [...specifiedDirectives, ...federationDirectives],
});
});

it('enforces unique union names', () => {
const [definitions] = createDocumentsForServices([
{
typeDefs: gql`
union UPC = String | Int
`,
name: 'serviceA',
},
{
typeDefs: gql`
union UPC = String | Int | Boolean
`,
name: 'serviceB',
},
]);

const errors = validateSDL(definitions, schema, [UniqueUnionTypes]);
expect(errors).toHaveLength(1);
expect(errors[0]).toMatchInlineSnapshot(`
Object {
"code": "VALUE_TYPE_UNION_TYPES_MISMATCH",
"message": "The union 'UPC' is defined in multiple places, however the unioned types do not match. Union types with the same name must also consist of identical types. The type Boolean is mismatched.",
}
`);
});

it('permits duplicate union names for identical union types', () => {
const [definitions] = createDocumentsForServices([
{
typeDefs: gql`
union UPC = String | Int
`,
name: 'serviceA',
},
{
typeDefs: gql`
union UPC = String | Int
`,
name: 'serviceB',
},
]);

const errors = validateSDL(definitions, schema, [UniqueUnionTypes]);
expect(errors).toHaveLength(0);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
graphqlErrorSerializer,
} from '../../../../snapshotSerializers';
import federationDirectives from '../../../../directives';
import { UniqueTypeNamesWithoutEnumsOrScalars } from '..';
import { UniqueTypeNamesWithFields } from '..';
import { ServiceDefinition } from '../../../types';
import { buildMapsFromServiceList } from '../../../compose';

Expand All @@ -36,7 +36,7 @@ function createDocumentsForServices(
];
}

describe('UniqueTypeNamesWithoutEnumsOrScalars', () => {
describe('UniqueTypeNamesWithFields', () => {
let schema: GraphQLSchema;

// create a blank schema for each test
Expand Down Expand Up @@ -69,7 +69,7 @@ describe('UniqueTypeNamesWithoutEnumsOrScalars', () => {
]);

const errors = validateSDL(definitions, schema, [
UniqueTypeNamesWithoutEnumsOrScalars,
UniqueTypeNamesWithFields,
]);
expect(errors).toHaveLength(1);
expect(errors[0].message).toMatch(
Expand Down Expand Up @@ -102,7 +102,7 @@ describe('UniqueTypeNamesWithoutEnumsOrScalars', () => {
]);

const errors = validateSDL(definitions, schema, [
UniqueTypeNamesWithoutEnumsOrScalars,
UniqueTypeNamesWithFields,
]);
expect(errors).toHaveLength(2);
expect(errors).toMatchInlineSnapshot(`
Expand Down Expand Up @@ -142,7 +142,7 @@ describe('UniqueTypeNamesWithoutEnumsOrScalars', () => {
]);

const errors = validateSDL(definitions, schema, [
UniqueTypeNamesWithoutEnumsOrScalars,
UniqueTypeNamesWithFields,
]);
expect(errors).toHaveLength(1);
expect(errors[0].message).toMatch(
Expand Down Expand Up @@ -171,42 +171,14 @@ describe('UniqueTypeNamesWithoutEnumsOrScalars', () => {
]);

const errors = validateSDL(definitions, schema, [
UniqueTypeNamesWithoutEnumsOrScalars,
UniqueTypeNamesWithFields,
]);
expect(errors).toHaveLength(1);
expect(errors[0].message).toMatch(
'There can be only one type named "Product".',
);
});

it('union definitions', () => {
const [definitions] = createDocumentsForServices([
{
typeDefs: gql`
union UPC = String | Int
`,
name: 'serviceA',
},
{
typeDefs: gql`
union UPC = String | Int | Boolean
`,
name: 'serviceB',
},
]);

const errors = validateSDL(definitions, schema, [
UniqueTypeNamesWithoutEnumsOrScalars,
]);
expect(errors).toHaveLength(1);
expect(errors[0]).toMatchInlineSnapshot(`
Object {
"code": "VALUE_TYPE_UNION_TYPES_MISMATCH",
"message": "The union 'UPC' is defined in multiple places, however the unioned types do not match. Union types with the same name must also consist of identical types. The type Boolean is mismatched.",
}
`);
});

it('input definitions', () => {
const [definitions] = createDocumentsForServices([
{
Expand All @@ -228,7 +200,7 @@ describe('UniqueTypeNamesWithoutEnumsOrScalars', () => {
]);

const errors = validateSDL(definitions, schema, [
UniqueTypeNamesWithoutEnumsOrScalars,
UniqueTypeNamesWithFields,
]);
expect(errors).toHaveLength(1);
expect(errors[0].message).toMatch(
Expand All @@ -255,7 +227,7 @@ describe('UniqueTypeNamesWithoutEnumsOrScalars', () => {
]);

const errors = validateSDL(definitions, schema, [
UniqueTypeNamesWithoutEnumsOrScalars,
UniqueTypeNamesWithFields,
]);
expect(errors).toHaveLength(0);
});
Expand All @@ -282,7 +254,7 @@ describe('UniqueTypeNamesWithoutEnumsOrScalars', () => {
]);

const errors = validateSDL(definitions, schema, [
UniqueTypeNamesWithoutEnumsOrScalars,
UniqueTypeNamesWithFields,
]);
expect(errors).toHaveLength(0);
});
Expand All @@ -308,7 +280,7 @@ describe('UniqueTypeNamesWithoutEnumsOrScalars', () => {
]);

const errors = validateSDL(definitions, schema, [
UniqueTypeNamesWithoutEnumsOrScalars,
UniqueTypeNamesWithFields,
]);
expect(errors).toHaveLength(0);
});
Expand All @@ -334,29 +306,7 @@ describe('UniqueTypeNamesWithoutEnumsOrScalars', () => {
]);

const errors = validateSDL(definitions, schema, [
UniqueTypeNamesWithoutEnumsOrScalars,
]);
expect(errors).toHaveLength(0);
});

it('identical union types', () => {
const [definitions] = createDocumentsForServices([
{
typeDefs: gql`
union UPC = String | Int
`,
name: 'serviceA',
},
{
typeDefs: gql`
union UPC = String | Int
`,
name: 'serviceB',
},
]);

const errors = validateSDL(definitions, schema, [
UniqueTypeNamesWithoutEnumsOrScalars,
UniqueTypeNamesWithFields,
]);
expect(errors).toHaveLength(0);
});
Expand Down Expand Up @@ -384,7 +334,7 @@ describe('UniqueTypeNamesWithoutEnumsOrScalars', () => {
]);

const errors = validateSDL(definitions, schema, [
UniqueTypeNamesWithoutEnumsOrScalars,
UniqueTypeNamesWithFields,
]);
expect(errors[0]).toMatchInlineSnapshot(`
Object {
Expand Down Expand Up @@ -415,7 +365,7 @@ describe('UniqueTypeNamesWithoutEnumsOrScalars', () => {
]);

const errors = validateSDL(definitions, schema, [
UniqueTypeNamesWithoutEnumsOrScalars,
UniqueTypeNamesWithFields,
]);
expect(errors).toHaveLength(1);
expect(errors[0]).toMatchInlineSnapshot(`
Expand Down Expand Up @@ -447,7 +397,7 @@ describe('UniqueTypeNamesWithoutEnumsOrScalars', () => {
]);

const errors = validateSDL(definitions, schema, [
UniqueTypeNamesWithoutEnumsOrScalars,
UniqueTypeNamesWithFields,
]);
expect(errors).toHaveLength(1);
expect(errors[0]).toMatchInlineSnapshot(`
Expand Down Expand Up @@ -479,7 +429,7 @@ describe('UniqueTypeNamesWithoutEnumsOrScalars', () => {
]);

const errors = validateSDL(definitions, schema, [
UniqueTypeNamesWithoutEnumsOrScalars,
UniqueTypeNamesWithFields,
]);
expect(errors).toHaveLength(0);
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
export {
UniqueTypeNamesWithoutEnumsOrScalars,
} from './uniqueTypeNamesWithoutEnumsOrScalars';
export { UniqueTypeNamesWithFields } from './uniqueTypeNamesWithFields';
export { MatchingEnums } from './matchingEnums';
export { PossibleTypeExtensions } from './possibleTypeExtensions';
export { UniqueFieldDefinitionNames } from './uniqueFieldDefinitionNames';
export { UniqueUnionTypes } from './matchingUnions';
Loading

0 comments on commit 1f00664

Please sign in to comment.