Skip to content

Commit

Permalink
Add matchingEnums to sdl validation rules
Browse files Browse the repository at this point in the history
  • Loading branch information
JakeDawkins committed Jun 14, 2019
1 parent bab5b34 commit 006fde4
Show file tree
Hide file tree
Showing 10 changed files with 531 additions and 259 deletions.
353 changes: 156 additions & 197 deletions packages/apollo-federation/src/composition/__tests__/compose.test.ts

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -206,46 +206,3 @@ it('errors on invalid usages of default operation names', () => {
});

it.todo('errors on duplicate types where there is a mismatch of field types');

fit('does not error with duplicate enums', () => {
const serviceA = {
typeDefs: gql`
type Query {
products: [Product]!
}
type Product @key(fields: "sku") {
sku: String!
upc: String!
type: ProductType
}
enum ProductType {
FURNITURE
BOOK
DIGITAL
}
`,
name: 'serviceA',
};

const serviceB = {
typeDefs: gql`
type Product @extends @key(fields: "sku") {
sku: String! @external
price: Int! @requires(fields: "sku")
productType: ProductType
}
enum ProductType {
FURNITURE
BOOK
DIGITAL
}
`,
name: 'serviceB',
};

const { schema, errors } = composeAndValidate([serviceA, serviceB]);
expect(errors).toHaveLength(0);
});
17 changes: 13 additions & 4 deletions packages/apollo-federation/src/composition/compose.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import {
GraphQLSchema,
extendSchema,
Kind,
TypeDefinitionNode,
TypeExtensionNode,
isTypeDefinitionNode,
isTypeExtensionNode,
Expand Down Expand Up @@ -33,13 +32,21 @@ import {
ServiceName,
ExternalFieldDefinition,
ServiceNameToKeyDirectivesMap,
FederatedTypeDefinitionNode,
} from './types';
import { validateSDL } from 'graphql/validation/validate';
import { compositionRules } from './rules';

// type FederatedTypeDefinitionNode = TypeDefinitionNode & {
// serviceName: string | null
// }
// Map of all definitions to eventually be passed to extendSchema
// interface DefinitionsMapEntry {
// definition: TypeDefinitionNode;
// serviceName: string | null;
// }
interface DefinitionsMap {
[name: string]: TypeDefinitionNode[];
[name: string]: FederatedTypeDefinitionNode[];
}
// Map of all extensions to eventually be passed to extendSchema
interface ExtensionsMap {
Expand Down Expand Up @@ -157,9 +164,9 @@ export function buildMapsFromServiceList(serviceList: ServiceDefinition[]) {
* take precedence). If not, create the definitions array and add it to the definitionsMap.
*/
if (definitionsMap[typeName]) {
definitionsMap[typeName].push(definition);
definitionsMap[typeName].push({ ...definition, serviceName });
} else {
definitionsMap[typeName] = [definition];
definitionsMap[typeName] = [{ ...definition, serviceName }];
}
} else if (isTypeExtensionNode(definition)) {
const typeName = definition.name.value;
Expand Down Expand Up @@ -240,6 +247,7 @@ export function buildMapsFromServiceList(serviceList: ServiceDefinition[]) {
kind: Kind.OBJECT_TYPE_DEFINITION,
name: { kind: Kind.NAME, value: extensionTypeName },
fields: [],
serviceName: null,
},
];

Expand All @@ -266,6 +274,7 @@ export function buildSchemaFromDefinitionsAndExtensions({
extensionsMap: ExtensionsMap;
}) {
let errors: GraphQLError[] | undefined = undefined;

let schema = new GraphQLSchema({
query: undefined,
directives: [...specifiedDirectives, ...federationDirectives],
Expand Down
7 changes: 5 additions & 2 deletions packages/apollo-federation/src/composition/rules.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { specifiedSDLRules } from 'graphql/validation/specifiedRules';

import { UniqueTypeNamesWithoutEnumsOrScalars } from './validate/sdl';
import {
UniqueTypeNamesWithoutEnumsOrScalars,
matchingEnums,
} from './validate/sdl';

const omit = [
'UniqueDirectivesPerLocation',
Expand All @@ -10,4 +13,4 @@ const omit = [

export const compositionRules = specifiedSDLRules
.filter(rule => !omit.includes(rule.name))
.concat([UniqueTypeNamesWithoutEnumsOrScalars]);
.concat([UniqueTypeNamesWithoutEnumsOrScalars, matchingEnums]);
11 changes: 10 additions & 1 deletion packages/apollo-federation/src/composition/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { SelectionNode, DocumentNode, FieldDefinitionNode } from 'graphql';
import {
SelectionNode,
DocumentNode,
FieldDefinitionNode,
TypeDefinitionNode,
} from 'graphql';

export type ServiceName = string | null;

Expand Down Expand Up @@ -74,3 +79,7 @@ declare module 'graphql/type/definition' {
federation?: FederationField;
}
}

export type FederatedTypeDefinitionNode = TypeDefinitionNode & {
serviceName: string | null;
};
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
import {
visit,
GraphQLError,
EnumTypeDefinitionNode,
ScalarTypeDefinitionNode,
} from 'graphql';
import { visit, GraphQLError } from 'graphql';
import { ServiceDefinition } from '../../types';

import { logServiceAndType, errorWithCode } from '../../utils';
Expand All @@ -15,8 +10,8 @@ export const duplicateEnumOrScalar = ({
const errors: GraphQLError[] = [];

// keep track of every enum and scalar and error if there are ever duplicates
const enums: EnumTypeDefinitionNode[] = [];
const scalars: ScalarTypeDefinitionNode[] = [];
const enums: string[] = [];
const scalars: string[] = [];

visit(typeDefs, {
EnumTypeDefinition(definition) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { visit, GraphQLError, EnumTypeDefinitionNode } from 'graphql';
import { visit, GraphQLError } from 'graphql';
import { ServiceDefinition } from '../../types';

import { logServiceAndType, errorWithCode } from '../../utils';
Expand All @@ -9,12 +9,15 @@ export const duplicateEnumValue = ({
}: ServiceDefinition) => {
const errors: GraphQLError[] = [];

const enums: Map<String, String[]> = {};
const enums: { [name: string]: string[] } = {};

visit(typeDefs, {
EnumTypeDefinition(definition) {
const name = definition.name.value;
const enumValues = definition.values.map(value => value.name.value);
const enumValues =
definition.values && definition.values.map(value => value.name.value);

if (!enumValues) return definition;

if (enums[name] && enums[name].length) {
enumValues.map(valueName => {
Expand All @@ -38,7 +41,10 @@ export const duplicateEnumValue = ({
},
EnumTypeExtension(definition) {
const name = definition.name.value;
const enumValues = definition.values.map(value => value.name.value);
const enumValues =
definition.values && definition.values.map(value => value.name.value);

if (!enumValues) return definition;

if (enums[name] && enums[name].length) {
enumValues.map(valueName => {
Expand Down
Loading

0 comments on commit 006fde4

Please sign in to comment.