@apollo/federation
Version:
Apollo Federation Utilities
411 lines (335 loc) • 10.3 kB
text/typescript
import gql from 'graphql-tag';
import {
defaultRootOperationTypes,
replaceExtendedDefinitionsWithExtensions,
normalizeTypeDefs,
stripCommonPrimitives,
} from '../normalize';
import { astSerializer } from 'apollo-federation-integration-testsuite';
import { specifiedDirectives } from 'graphql';
expect.addSnapshotSerializer(astSerializer);
describe('SDL normalization and its respective parts', () => {
describe('defaultRootOperationTypes', () => {
it('transforms defined root operation types to respective extended default root operation types', () => {
const typeDefs = gql`
schema {
query: RootQuery
mutation: RootMutation
}
type RootQuery {
product: Product
}
type Product {
sku: String
}
type RootMutation {
updateProduct: Product
}
`;
const schemaWithDefaultedRootOperationTypes = defaultRootOperationTypes(
typeDefs,
);
expect(schemaWithDefaultedRootOperationTypes).toMatchInlineSnapshot(`
extend type Query {
product: Product
}
type Product {
sku: String
}
extend type Mutation {
updateProduct: Product
}
`);
});
it('removes all types using a default root operation type name when a schema definition is provided (root types are defined by the user)', () => {
const typeDefs = gql`
schema {
query: RootQuery
}
type RootQuery {
product: Product
}
type Product {
sku: String
}
type Query {
removeThisEntireType: String
}
type Mutation {
removeThisEntireType: String
}
type Subscription {
removeThisEntireType: String
}
`;
const schemaWithDefaultedRootOperationTypes = defaultRootOperationTypes(
typeDefs,
);
expect(schemaWithDefaultedRootOperationTypes).toMatchInlineSnapshot(`
extend type Query {
product: Product
}
type Product {
sku: String
}
`);
});
it('drops fields that reference an invalid default root operation type name', () => {
const typeDefs = gql`
schema {
query: RootQuery
mutation: RootMutation
}
type RootQuery {
product: Product
}
type Query {
removeThisEntireType: String
}
type RootMutation {
keepThisField: String
removeThisField: Query
}
`;
const schemaWithDefaultedRootOperationTypes = defaultRootOperationTypes(
typeDefs,
);
expect(schemaWithDefaultedRootOperationTypes).toMatchInlineSnapshot(`
extend type Query {
product: Product
}
extend type Mutation {
keepThisField: String
}
`);
});
});
describe('replaceExtendedDefinitionsWithExtensions', () => {
it('transforms the @extends directive into type extensions', () => {
const typeDefs = gql`
type Product (fields: "sku") {
sku: String
}
`;
expect(replaceExtendedDefinitionsWithExtensions(typeDefs))
.toMatchInlineSnapshot(`
extend type Product @key(fields: "sku") {
sku: String @external
}
`);
});
});
describe('stripCommonPrimitives', () => {
it(`removes all common directive definitions`, () => {
// Detecting >15.1.0 by the new addition of the `specifiedBy` directive
const isAtLeastGraphqlVersionFifteenPointOne =
specifiedDirectives.length >= 4;
const typeDefs = gql`
# Default directives
# This directive needs to be conditionally added depending on the testing
# environment's version of graphql (>= 15.1.0 includes this new directive)
${isAtLeastGraphqlVersionFifteenPointOne
? 'directive @specifiedBy(url: String!) on SCALAR'
: ''}
directive (
reason: String = "No longer supported"
) on FIELD_DEFINITION | ENUM_VALUE
directive (
if: String = "Included when true."
) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT
directive (
if: String = "Skipped when true."
) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT
# Federation directives
directive (fields: _FieldSet!) repeatable on OBJECT | INTERFACE
directive on FIELD_DEFINITION
directive (fields: _FieldSet!) on FIELD_DEFINITION
directive (fields: _FieldSet!) on FIELD_DEFINITION
directive on OBJECT | INTERFACE
type Query {
thing: String
}
`;
expect(stripCommonPrimitives(typeDefs)).toMatchInlineSnapshot(`
type Query {
thing: String
}
`);
});
it(`doesn't remove custom directive definitions`, () => {
const typeDefs = gql`
directive on OBJECT
type Query {
thing: String
}
`;
expect(stripCommonPrimitives(typeDefs)).toMatchInlineSnapshot(`
directive @custom on OBJECT
type Query {
thing: String
}
`);
});
it(`removes all federation type definitions (scalars, unions, object types)`, () => {
const typeDefs = gql`
scalar _Any
scalar _FieldSet
union _Entity
type _Service {
sdl: String
}
type Query {
thing: String
}
`;
expect(stripCommonPrimitives(typeDefs)).toMatchInlineSnapshot(`
type Query {
thing: String
}
`);
});
it(`doesn't remove custom scalar, union, or object type definitions`, () => {
const typeDefs = gql`
scalar CustomScalar
type CustomType {
field: String!
}
union CustomUnion
type Query {
thing: String
}
`;
expect(stripCommonPrimitives(typeDefs)).toMatchInlineSnapshot(`
scalar CustomScalar
type CustomType {
field: String!
}
union CustomUnion
type Query {
thing: String
}
`);
});
it(`removes all federation field definitions (_service, _entities)`, () => {
const typeDefs = gql`
type Query {
_service: _Service!
_entities(representations: [_Any!]!): [_Entity]!
thing: String
}
`;
expect(stripCommonPrimitives(typeDefs)).toMatchInlineSnapshot(`
type Query {
thing: String
}
`);
});
it(`removes the Query type altogether if it has no fields left after normalization`, () => {
const typeDefs = gql`
type Query {
_service: _Service!
_entities(representations: [_Any!]!): [_Entity]!
}
type Custom {
field: String
}
`;
expect(stripCommonPrimitives(typeDefs)).toMatchInlineSnapshot(`
type Custom {
field: String
}
`);
});
});
describe('normalizeTypeDefs', () => {
it('integration', () => {
// Detecting >15.1.0 by the new addition of the `specifiedBy` directive
const isAtLeastGraphqlVersionFifteenPointOne =
specifiedDirectives.length >= 4;
const typeDefsToNormalize = gql`
# Default directives
# This directive needs to be conditionally added depending on the testing
# environment's version of graphql (>= 15.1.0 includes this new directive)
${isAtLeastGraphqlVersionFifteenPointOne
? 'directive @specifiedBy(url: String!) on SCALAR'
: ''}
directive (
reason: String = "No longer supported"
) on FIELD_DEFINITION | ENUM_VALUE
directive (
if: String = "Included when true."
) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT
directive (
if: String = "Skipped when true."
) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT
directive (fields: _FieldSet!) repeatable on OBJECT | INTERFACE
directive on FIELD_DEFINITION
directive (fields: _FieldSet!) on FIELD_DEFINITION
directive (fields: _FieldSet!) on FIELD_DEFINITION
directive on OBJECT | INTERFACE
scalar _Any
scalar _FieldSet
union _Entity
type _Service {
sdl: String
}
schema {
query: RootQuery
mutation: RootMutation
}
type RootQuery {
_service: _Service!
_entities(representations: [_Any!]!): [_Entity]!
product: Product
}
type Product (fields: "sku") {
sku: String
}
type RootMutation {
updateProduct: Product
}
`;
const normalized = normalizeTypeDefs(typeDefsToNormalize);
expect(normalized).toMatchInlineSnapshot(`
extend type Query {
product: Product
}
extend type Product @key(fields: "sku") {
sku: String @external
}
extend type Mutation {
updateProduct: Product
}
`);
});
it('should allow schema describing default types', () => {
const typeDefsToNormalize = gql`
schema {
query: Query
mutation: Mutation
}
type Query {
product: Product
}
type Product (fields: "sku") {
sku: String
}
type Mutation {
updateProduct: Product
}
`;
const normalized = normalizeTypeDefs(typeDefsToNormalize);
expect(normalized).toMatchInlineSnapshot(`
extend type Query {
product: Product
}
extend type Product @key(fields: "sku") {
sku: String @external
}
extend type Mutation {
updateProduct: Product
}
`);
});
});
});