diff --git a/src/utilities/__tests__/findBreakingChanges-test.js b/src/utilities/__tests__/findBreakingChanges-test.js index e9fdde98bf..26a5205e64 100644 --- a/src/utilities/__tests__/findBreakingChanges-test.js +++ b/src/utilities/__tests__/findBreakingChanges-test.js @@ -32,6 +32,7 @@ import { findTypesRemovedFromUnions, findTypesThatChangedKind, findValuesRemovedFromEnums, + findValuesAddedToEnums, findArgChanges, findInterfacesRemovedFromObjectTypes, } from '../findBreakingChanges'; @@ -1349,7 +1350,63 @@ describe('findDangerousChanges', () => { }); }); + it('should detect if a value was added to an enum type', () => { + const oldEnumType = new GraphQLEnumType({ + name: 'EnumType1', + values: { + VALUE0: { value: 0 }, + VALUE1: { value: 1 }, + } + }); + const newEnumType = new GraphQLEnumType({ + name: 'EnumType1', + values: { + VALUE0: { value: 0 }, + VALUE1: { value: 1 }, + VALUE2: { value: 2 }, + } + }); + + const oldSchema = new GraphQLSchema({ + query: queryType, + types: [ + oldEnumType, + ] + }); + const newSchema = new GraphQLSchema({ + query: queryType, + types: [ + newEnumType, + ] + }); + + expect(findValuesAddedToEnums(oldSchema, newSchema)).to.eql( + [ + { + type: DangerousChangeType.VALUE_ADDED_TO_ENUM, + description: 'VALUE2 was added to enum type EnumType1.', + } + ] + ); + }); + it('should find all dangerous changes', () => { + const enumThatGainsAValueOld = new GraphQLEnumType({ + name: 'EnumType1', + values: { + VALUE0: { value: 0 }, + VALUE1: { value: 1 }, + } + }); + const enumThatGainsAValueNew = new GraphQLEnumType({ + name: 'EnumType1', + values: { + VALUE0: { value: 0 }, + VALUE1: { value: 1 }, + VALUE2: { value: 2 }, + } + }); + const oldType = new GraphQLObjectType({ name: 'Type1', fields: { @@ -1384,6 +1441,7 @@ describe('findDangerousChanges', () => { query: queryType, types: [ oldType, + enumThatGainsAValueOld ] }); @@ -1391,6 +1449,7 @@ describe('findDangerousChanges', () => { query: queryType, types: [ newType, + enumThatGainsAValueNew ] }); @@ -1398,6 +1457,10 @@ describe('findDangerousChanges', () => { { description: 'Type1.field1 arg name has changed defaultValue', type: 'ARG_DEFAULT_VALUE_CHANGE' + }, + { + description: 'VALUE2 was added to enum type EnumType1.', + type: 'VALUE_ADDED_TO_ENUM', } ]; diff --git a/src/utilities/findBreakingChanges.js b/src/utilities/findBreakingChanges.js index 6014597464..a6b137a9cd 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' }; export type BreakingChange = { @@ -85,6 +86,7 @@ export function findDangerousChanges( ): Array { return [ ...findArgChanges(oldSchema, newSchema).dangerousChanges, + ...findValuesAddedToEnums(oldSchema, newSchema) ]; } @@ -542,6 +544,42 @@ export function findValuesRemovedFromEnums( return valuesRemovedFromEnums; } +/** + * Given two schemas, returns an Array containing descriptions of any dangerous + * changes in the newSchema related to adding values to an enum type. + */ +export function findValuesAddedToEnums( + oldSchema: GraphQLSchema, + newSchema: GraphQLSchema +): Array { + const oldTypeMap = oldSchema.getTypeMap(); + const newTypeMap = newSchema.getTypeMap(); + + const valuesAddedToEnums = []; + Object.keys(oldTypeMap).forEach(typeName => { + const oldType = oldTypeMap[typeName]; + const newType = newTypeMap[typeName]; + if (!(oldType instanceof GraphQLEnumType) || + !(newType instanceof GraphQLEnumType)) { + return; + } + + const valuesInOldEnum = Object.create(null); + oldType.getValues().forEach(value => { + valuesInOldEnum[value.name] = true; + }); + newType.getValues().forEach(value => { + if (!valuesInOldEnum[value.name]) { + valuesAddedToEnums.push({ + type: DangerousChangeType.VALUE_ADDED_TO_ENUM, + description: `${value.name} was added to enum type ${typeName}.` + }); + } + }); + }); + return valuesAddedToEnums; +} + export function findInterfacesRemovedFromObjectTypes( oldSchema: GraphQLSchema, newSchema: GraphQLSchema