diff --git a/src/utilities/__tests__/findBreakingChanges-test.js b/src/utilities/__tests__/findBreakingChanges-test.js index 32d969a193..d6f2b1133e 100644 --- a/src/utilities/__tests__/findBreakingChanges-test.js +++ b/src/utilities/__tests__/findBreakingChanges-test.js @@ -1039,6 +1039,62 @@ describe('findDangerousChanges', () => { }, ]); }); + it("should return empty result if an argument's defaultValue is an Array and it's not changed", () => { + const oldSchema = buildSchema(` + input Input1 { + inner_array: [String] + inner_input: Input2 + inner_input_array: [Input2] + } + + input Input2 { + string_field: String + int_field: Int + } + + type Type1 { + empty_array_default(arr: [String!] = []): String + value_array_default(arr: [[String]] = [["a", "b"], ["c", "d"]]): String + input_default(input: Input1 = { + inner_array: ["a", "b"] + inner_input: {string_field: "abc", int_field: 32} + inner_input_array: [{int_field: 10}] + }): String + } + + type Query { + t: Type1 + } + `); + + const newSchema = buildSchema(` + input Input1 { + inner_array: [String] + inner_input: Input2 + inner_input_array: [Input2] + } + + input Input2 { + string_field: String + int_field: Int + } + + type Type1 { + empty_array_default(arr: [String!] = []): String + value_array_default(arr: [[String]] = [["a", "b"], ["c", "d"]]): String + input_default(input: Input1 = { + inner_array: ["a", "b"] + inner_input: {string_field: "abc", int_field: 32} + inner_input_array: [{int_field: 10}] + }): String + } + + type Query { + t: Type1 + } + `); + expect(findArgChanges(oldSchema, newSchema).dangerousChanges).to.eql([]); + }); }); it('should detect if a value was added to an enum type', () => { diff --git a/src/utilities/findBreakingChanges.js b/src/utilities/findBreakingChanges.js index 9f7962138f..6adcb0ffe8 100644 --- a/src/utilities/findBreakingChanges.js +++ b/src/utilities/findBreakingChanges.js @@ -232,8 +232,10 @@ export function findArgChanges( `${oldArgDef.type.toString()} to ${newArgDef.type.toString()}`, }); } else if ( - oldArgDef.defaultValue !== undefined && - oldArgDef.defaultValue !== newArgDef.defaultValue + !isChangeSafeForDefaultValue( + oldArgDef.defaultValue, + newArgDef.defaultValue, + ) ) { dangerousChanges.push({ type: DangerousChangeType.ARG_DEFAULT_VALUE_CHANGE, @@ -496,6 +498,41 @@ function isChangeSafeForInputObjectFieldOrFieldArg( return false; } +function isChangeSafeForDefaultValue( + oldDefaultValue: ?mixed, + newDefaultValue: ?mixed, +): boolean { + if (oldDefaultValue === undefined) { + return true; + } + + if (oldDefaultValue === newDefaultValue) { + return true; + } + + if ( + oldDefaultValue != null && + newDefaultValue != null && + typeof oldDefaultValue === 'object' && + typeof newDefaultValue === 'object' + ) { + if (Array.isArray(oldDefaultValue) !== Array.isArray(newDefaultValue)) { + return false; + } + + for (const key in oldDefaultValue) { + if ( + !isChangeSafeForDefaultValue(oldDefaultValue[key], newDefaultValue[key]) + ) { + return false; + } + } + return true; + } + + return false; +} + /** * Given two schemas, returns an Array containing descriptions of any breaking * changes in the newSchema related to removing types from a union type.