diff --git a/src/utilities/__tests__/findBreakingChanges-test.js b/src/utilities/__tests__/findBreakingChanges-test.js index 7021f34958..0d2580dd20 100644 --- a/src/utilities/__tests__/findBreakingChanges-test.js +++ b/src/utilities/__tests__/findBreakingChanges-test.js @@ -34,6 +34,7 @@ import { findValuesAddedToEnums, findArgChanges, findInterfacesRemovedFromObjectTypes, + findInterfacesAddedToObjectTypes, } from '../findBreakingChanges'; describe('findBreakingChanges', () => { @@ -1427,6 +1428,60 @@ describe('findDangerousChanges', () => { ); }); + it('should detect interfaces added to types', () => { + const interface1 = new GraphQLInterfaceType({ + name: 'Interface1', + fields: { + field1: { type: GraphQLString }, + }, + resolveType: () => null, + }); + const oldType = new GraphQLObjectType({ + name: 'Type1', + interfaces: [], + fields: { + field1: { + type: GraphQLString, + }, + }, + }); + + const newType = new GraphQLObjectType({ + name: 'Type1', + interfaces: [ + interface1 + ], + fields: { + field1: { + type: GraphQLString, + }, + }, + }); + + const oldSchema = new GraphQLSchema({ + query: queryType, + types: [ + oldType, + ] + }); + + const newSchema = new GraphQLSchema({ + query: queryType, + types: [ + newType, + ] + }); + + expect( + findInterfacesAddedToObjectTypes(oldSchema, newSchema) + ).to.eql([ + { + description: 'Interface1 added to interfaces implemented by Type1.', + type: DangerousChangeType.INTERFACE_ADDED_TO_OBJECT + } + ]); + }); + it('should detect if a type was added to a union type', () => { const type1 = new GraphQLObjectType({ name: 'Type1', @@ -1551,11 +1606,42 @@ describe('findDangerousChanges', () => { }, }); + const interface1 = new GraphQLInterfaceType({ + name: 'Interface1', + fields: { + field1: { type: GraphQLString }, + }, + resolveType: () => null, + }); + + const typeThatGainsInterfaceOld = new GraphQLObjectType({ + name: 'TypeThatGainsInterface1', + interfaces: [], + fields: { + field1: { + type: GraphQLString, + }, + }, + }); + + const typeThaGainsInterfaceNew = new GraphQLObjectType({ + name: 'TypeThatGainsInterface1', + interfaces: [ + interface1 + ], + fields: { + field1: { + type: GraphQLString, + }, + }, + }); + const oldSchema = new GraphQLSchema({ query: queryType, types: [ oldType, enumThatGainsAValueOld, + typeThatGainsInterfaceOld, unionTypeThatGainsATypeOld ] }); @@ -1565,6 +1651,7 @@ describe('findDangerousChanges', () => { types: [ newType, enumThatGainsAValueNew, + typeThaGainsInterfaceNew, unionTypeThatGainsATypeNew ] }); @@ -1578,6 +1665,11 @@ describe('findDangerousChanges', () => { description: 'VALUE2 was added to enum type EnumType1.', type: 'VALUE_ADDED_TO_ENUM', }, + { + description: 'Interface1 added to interfaces implemented ' + + 'by TypeThatGainsInterface1.', + type: DangerousChangeType.INTERFACE_ADDED_TO_OBJECT + }, { type: DangerousChangeType.TYPE_ADDED_TO_UNION, description: 'TypeInUnion2 was added to union type ' + diff --git a/src/utilities/findBreakingChanges.js b/src/utilities/findBreakingChanges.js index f4d6f86848..be12154922 100644 --- a/src/utilities/findBreakingChanges.js +++ b/src/utilities/findBreakingChanges.js @@ -44,6 +44,7 @@ export const BreakingChangeType = { export const DangerousChangeType = { ARG_DEFAULT_VALUE_CHANGE: 'ARG_DEFAULT_VALUE_CHANGE', VALUE_ADDED_TO_ENUM: 'VALUE_ADDED_TO_ENUM', + INTERFACE_ADDED_TO_OBJECT: 'INTERFACE_ADDED_TO_OBJECT', TYPE_ADDED_TO_UNION: 'TYPE_ADDED_TO_UNION', }; @@ -87,7 +88,8 @@ export function findDangerousChanges( return [ ...findArgChanges(oldSchema, newSchema).dangerousChanges, ...findValuesAddedToEnums(oldSchema, newSchema), - ...findTypesAddedToUnions(oldSchema, newSchema) + ...findInterfacesAddedToObjectTypes(oldSchema, newSchema), + ...findTypesAddedToUnions(oldSchema, newSchema), ]; } @@ -647,3 +649,36 @@ export function findInterfacesRemovedFromObjectTypes( }); return breakingChanges; } + +export function findInterfacesAddedToObjectTypes( + oldSchema: GraphQLSchema, + newSchema: GraphQLSchema +): Array { + const oldTypeMap = oldSchema.getTypeMap(); + const newTypeMap = newSchema.getTypeMap(); + const interfacesAddedToObjectTypes = []; + + Object.keys(newTypeMap).forEach(typeName => { + const oldType = oldTypeMap[typeName]; + const newType = newTypeMap[typeName]; + if ( + !(oldType instanceof GraphQLObjectType) || + !(newType instanceof GraphQLObjectType) + ) { + return; + } + + const oldInterfaces = oldType.getInterfaces(); + const newInterfaces = newType.getInterfaces(); + newInterfaces.forEach(newInterface => { + if (!oldInterfaces.some(int => int.name === newInterface.name)) { + interfacesAddedToObjectTypes.push({ + type: DangerousChangeType.INTERFACE_ADDED_TO_OBJECT, + description: `${newInterface.name} added to interfaces implemented ` + + `by ${typeName}.` + }); + } + }); + }); + return interfacesAddedToObjectTypes; +}