Skip to content

Commit fc0c06c

Browse files
findBreakingChanges: use string representation to compare default values (#1916)
Fixes #1567 Closes #1593
1 parent e2fe67e commit fc0c06c

File tree

2 files changed

+128
-17
lines changed

2 files changed

+128
-17
lines changed

src/utilities/__tests__/findBreakingChanges-test.js

Lines changed: 96 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -820,27 +820,116 @@ describe('findBreakingChanges', () => {
820820
});
821821

822822
describe('findDangerousChanges', () => {
823-
it("should detect if an argument's defaultValue has changed", () => {
824-
const oldSchema = buildSchema(`
823+
it('should detect if a defaultValue changed on an argument', () => {
824+
const oldSDL = `
825+
input Input1 {
826+
innerInputArray: [Input2]
827+
}
828+
829+
input Input2 {
830+
arrayField: [Int]
831+
}
832+
825833
type Type1 {
826-
field1(name: String = "test"): String
834+
field1(
835+
withDefaultValue: String = "TO BE DELETED"
836+
stringArg: String = "test"
837+
emptyArray: [Int!] = []
838+
valueArray: [[String]] = [["a", "b"], ["c"]]
839+
complexObject: Input1 = {
840+
innerInputArray: [{ arrayField: [1, 2, 3] }]
841+
}
842+
): String
827843
}
828-
`);
844+
`;
845+
846+
const oldSchema = buildSchema(oldSDL);
847+
const copyOfOldSchema = buildSchema(oldSDL);
848+
expect(findDangerousChanges(oldSchema, copyOfOldSchema)).to.deep.equal([]);
829849

830850
const newSchema = buildSchema(`
851+
input Input1 {
852+
innerInputArray: [Input2]
853+
}
854+
855+
input Input2 {
856+
arrayField: [Int]
857+
}
858+
831859
type Type1 {
832-
field1(name: String = "Test"): String
860+
field1(
861+
withDefaultValue: String
862+
stringArg: String = "Test"
863+
emptyArray: [Int!] = [7]
864+
valueArray: [[String]] = [["b", "a"], ["d"]]
865+
complexObject: Input1 = {
866+
innerInputArray: [{ arrayField: [3, 2, 1] }]
867+
}
868+
): String
833869
}
834870
`);
835871

836872
expect(findDangerousChanges(oldSchema, newSchema)).to.deep.equal([
837873
{
838874
type: DangerousChangeType.ARG_DEFAULT_VALUE_CHANGE,
839-
description: 'Type1.field1 arg name has changed defaultValue.',
875+
description:
876+
'Type1.field1 arg withDefaultValue defaultValue was removed.',
877+
},
878+
{
879+
type: DangerousChangeType.ARG_DEFAULT_VALUE_CHANGE,
880+
description:
881+
'Type1.field1 arg stringArg has changed defaultValue from "test" to "Test".',
882+
},
883+
{
884+
type: DangerousChangeType.ARG_DEFAULT_VALUE_CHANGE,
885+
description:
886+
'Type1.field1 arg emptyArray has changed defaultValue from [] to [7].',
887+
},
888+
{
889+
type: DangerousChangeType.ARG_DEFAULT_VALUE_CHANGE,
890+
description:
891+
'Type1.field1 arg valueArray has changed defaultValue from [["a", "b"], ["c"]] to [["b", "a"], ["d"]].',
892+
},
893+
{
894+
type: DangerousChangeType.ARG_DEFAULT_VALUE_CHANGE,
895+
description:
896+
'Type1.field1 arg complexObject has changed defaultValue from {innerInputArray: [{arrayField: [1, 2, 3]}]} to {innerInputArray: [{arrayField: [3, 2, 1]}]}.',
840897
},
841898
]);
842899
});
843900

901+
it('should ignore changes in field order of defaultValue', () => {
902+
const oldSchema = buildSchema(`
903+
input Input1 {
904+
a: String
905+
b: String
906+
c: String
907+
}
908+
909+
type Type1 {
910+
field1(
911+
arg1: Input1 = { a: "a", b: "b", c: "c" }
912+
): String
913+
}
914+
`);
915+
916+
const newSchema = buildSchema(`
917+
input Input1 {
918+
a: String
919+
b: String
920+
c: String
921+
}
922+
923+
type Type1 {
924+
field1(
925+
arg1: Input1 = { c: "c", b: "b", a: "a" }
926+
): String
927+
}
928+
`);
929+
930+
expect(findDangerousChanges(oldSchema, newSchema)).to.deep.equal([]);
931+
});
932+
844933
it('should detect if a value was added to an enum type', () => {
845934
const oldSchema = buildSchema(`
846935
enum EnumType1 {
@@ -979,7 +1068,7 @@ describe('findDangerousChanges', () => {
9791068
{
9801069
type: DangerousChangeType.ARG_DEFAULT_VALUE_CHANGE,
9811070
description:
982-
'Type1.field1 arg argThatChangesDefaultValue has changed defaultValue.',
1071+
'Type1.field1 arg argThatChangesDefaultValue has changed defaultValue from "test" to "Test".',
9831072
},
9841073
{
9851074
type: DangerousChangeType.INTERFACE_ADDED_TO_OBJECT,

src/utilities/findBreakingChanges.js

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,12 @@
1010
import objectValues from '../polyfills/objectValues';
1111
import keyMap from '../jsutils/keyMap';
1212
import inspect from '../jsutils/inspect';
13+
import invariant from '../jsutils/invariant';
14+
import { print } from '../language/printer';
1315
import {
1416
type GraphQLField,
1517
type GraphQLType,
18+
type GraphQLInputType,
1619
type GraphQLNamedType,
1720
type GraphQLEnumType,
1821
type GraphQLUnionType,
@@ -32,6 +35,7 @@ import {
3235
isRequiredInputField,
3336
} from '../type/definition';
3437
import { type GraphQLSchema } from '../type/schema';
38+
import { astFromValue } from './astFromValue';
3539

3640
export const BreakingChangeType = Object.freeze({
3741
TYPE_REMOVED: 'TYPE_REMOVED',
@@ -404,16 +408,28 @@ function findArgChanges(
404408
`${oldArg.name} has changed type from ` +
405409
`${String(oldArg.type)} to ${String(newArg.type)}.`,
406410
});
407-
} else if (
408-
oldArg.defaultValue !== undefined &&
409-
oldArg.defaultValue !== newArg.defaultValue
410-
) {
411-
schemaChanges.push({
412-
type: DangerousChangeType.ARG_DEFAULT_VALUE_CHANGE,
413-
description:
414-
`${oldType.name}.${oldField.name} arg ` +
415-
`${oldArg.name} has changed defaultValue.`,
416-
});
411+
} else if (oldArg.defaultValue !== undefined) {
412+
if (newArg.defaultValue === undefined) {
413+
schemaChanges.push({
414+
type: DangerousChangeType.ARG_DEFAULT_VALUE_CHANGE,
415+
description:
416+
`${oldType.name}.${oldField.name} arg ` +
417+
`${oldArg.name} defaultValue was removed.`,
418+
});
419+
} else {
420+
const oldValueStr = stringifyValue(oldArg.defaultValue, oldArg.type);
421+
const newValueStr = stringifyValue(newArg.defaultValue, newArg.type);
422+
423+
if (oldValueStr !== newValueStr) {
424+
schemaChanges.push({
425+
type: DangerousChangeType.ARG_DEFAULT_VALUE_CHANGE,
426+
description:
427+
`${oldType.name}.${oldField.name} arg ` +
428+
`${oldArg.name} has changed defaultValue ` +
429+
`from ${oldValueStr} to ${newValueStr}.`,
430+
});
431+
}
432+
}
417433
}
418434
}
419435

@@ -529,6 +545,12 @@ function typeKindName(type: GraphQLNamedType): string {
529545
throw new TypeError(`Unexpected type: ${inspect((type: empty))}.`);
530546
}
531547

548+
function stringifyValue(value: mixed, type: GraphQLInputType): string {
549+
const ast = astFromValue(value, type);
550+
invariant(ast != null);
551+
return print(ast);
552+
}
553+
532554
function diff<T: { name: string }>(
533555
oldArray: $ReadOnlyArray<T>,
534556
newArray: $ReadOnlyArray<T>,

0 commit comments

Comments
 (0)