@@ -46,6 +46,8 @@ export const DangerousChangeType = {
46
46
VALUE_ADDED_TO_ENUM : 'VALUE_ADDED_TO_ENUM' ,
47
47
INTERFACE_ADDED_TO_OBJECT : 'INTERFACE_ADDED_TO_OBJECT' ,
48
48
TYPE_ADDED_TO_UNION : 'TYPE_ADDED_TO_UNION' ,
49
+ NULLABLE_INPUT_FIELD_ADDED : 'NULLABLE_INPUT_FIELD_ADDED' ,
50
+ NULLABLE_ARG_ADDED : 'NULLABLE_ARG_ADDED' ,
49
51
} ;
50
52
51
53
export type BreakingChange = {
@@ -69,7 +71,9 @@ export function findBreakingChanges(
69
71
return [
70
72
...findRemovedTypes ( oldSchema , newSchema ) ,
71
73
...findTypesThatChangedKind ( oldSchema , newSchema ) ,
72
- ...findFieldsThatChangedType ( oldSchema , newSchema ) ,
74
+ ...findFieldsThatChangedTypeOnObjectOrInterfaceTypes ( oldSchema , newSchema ) ,
75
+ ...findFieldsThatChangedTypeOnInputObjectTypes ( oldSchema , newSchema )
76
+ . breakingChanges ,
73
77
...findTypesRemovedFromUnions ( oldSchema , newSchema ) ,
74
78
...findValuesRemovedFromEnums ( oldSchema , newSchema ) ,
75
79
...findArgChanges ( oldSchema , newSchema ) . breakingChanges ,
@@ -90,6 +94,8 @@ export function findDangerousChanges(
90
94
...findValuesAddedToEnums ( oldSchema , newSchema ) ,
91
95
...findInterfacesAddedToObjectTypes ( oldSchema , newSchema ) ,
92
96
...findTypesAddedToUnions ( oldSchema , newSchema ) ,
97
+ ...findFieldsThatChangedTypeOnInputObjectTypes ( oldSchema , newSchema )
98
+ . dangerousChanges
93
99
] ;
94
100
}
95
101
@@ -224,12 +230,20 @@ export function findArgChanges(
224
230
const oldArgDef = oldArgs . find (
225
231
arg => arg . name === newArgDef . name
226
232
) ;
227
- if ( ! oldArgDef && newArgDef . type instanceof GraphQLNonNull ) {
228
- breakingChanges . push ( {
229
- type : BreakingChangeType . NON_NULL_ARG_ADDED ,
230
- description : `A non-null arg ${ newArgDef . name } on ` +
231
- `${ newType . name } .${ fieldName } was added` ,
232
- } ) ;
233
+ if ( ! oldArgDef ) {
234
+ if ( newArgDef . type instanceof GraphQLNonNull ) {
235
+ breakingChanges . push ( {
236
+ type : BreakingChangeType . NON_NULL_ARG_ADDED ,
237
+ description : `A non-null arg ${ newArgDef . name } on ` +
238
+ `${ newType . name } .${ fieldName } was added` ,
239
+ } ) ;
240
+ } else {
241
+ dangerousChanges . push ( {
242
+ type : DangerousChangeType . NULLABLE_ARG_ADDED ,
243
+ description : `A nullable arg ${ newArgDef . name } on ` +
244
+ `${ newType . name } .${ fieldName } was added` ,
245
+ } ) ;
246
+ }
233
247
}
234
248
} ) ;
235
249
} ) ;
@@ -263,30 +277,14 @@ function typeKindName(type: GraphQLNamedType): string {
263
277
throw new TypeError ( 'Unknown type ' + type . constructor . name ) ;
264
278
}
265
279
266
- /**
267
- * Given two schemas, returns an Array containing descriptions of any breaking
268
- * changes in the newSchema related to the fields on a type. This includes if
269
- * a field has been removed from a type, if a field has changed type, or if
270
- * a non-null field is added to an input type.
271
- */
272
- export function findFieldsThatChangedType (
273
- oldSchema : GraphQLSchema ,
274
- newSchema : GraphQLSchema
275
- ) : Array < BreakingChange > {
276
- return [
277
- ...findFieldsThatChangedTypeOnObjectOrInterfaceTypes ( oldSchema , newSchema ) ,
278
- ...findFieldsThatChangedTypeOnInputObjectTypes ( oldSchema , newSchema ) ,
279
- ] ;
280
- }
281
-
282
- function findFieldsThatChangedTypeOnObjectOrInterfaceTypes (
280
+ export function findFieldsThatChangedTypeOnObjectOrInterfaceTypes (
283
281
oldSchema : GraphQLSchema ,
284
282
newSchema : GraphQLSchema ,
285
283
) : Array < BreakingChange > {
286
284
const oldTypeMap = oldSchema . getTypeMap ( ) ;
287
285
const newTypeMap = newSchema . getTypeMap ( ) ;
288
286
289
- const breakingFieldChanges = [ ] ;
287
+ const breakingChanges = [ ] ;
290
288
Object . keys ( oldTypeMap ) . forEach ( typeName => {
291
289
const oldType = oldTypeMap [ typeName ] ;
292
290
const newType = newTypeMap [ typeName ] ;
@@ -303,7 +301,7 @@ function findFieldsThatChangedTypeOnObjectOrInterfaceTypes(
303
301
Object . keys ( oldTypeFieldsDef ) . forEach ( fieldName => {
304
302
// Check if the field is missing on the type in the new schema.
305
303
if ( ! ( fieldName in newTypeFieldsDef ) ) {
306
- breakingFieldChanges . push ( {
304
+ breakingChanges . push ( {
307
305
type : BreakingChangeType . FIELD_REMOVED ,
308
306
description : `${ typeName } .${ fieldName } was removed.` ,
309
307
} ) ;
@@ -319,7 +317,7 @@ function findFieldsThatChangedTypeOnObjectOrInterfaceTypes(
319
317
const newFieldTypeString = isNamedType ( newFieldType ) ?
320
318
newFieldType . name :
321
319
newFieldType . toString ( ) ;
322
- breakingFieldChanges . push ( {
320
+ breakingChanges . push ( {
323
321
type : BreakingChangeType . FIELD_CHANGED_KIND ,
324
322
description : `${ typeName } .${ fieldName } changed type from ` +
325
323
`${ oldFieldTypeString } to ${ newFieldTypeString } .` ,
@@ -328,17 +326,21 @@ function findFieldsThatChangedTypeOnObjectOrInterfaceTypes(
328
326
}
329
327
} ) ;
330
328
} ) ;
331
- return breakingFieldChanges ;
329
+ return breakingChanges ;
332
330
}
333
331
334
332
export function findFieldsThatChangedTypeOnInputObjectTypes (
335
333
oldSchema : GraphQLSchema ,
336
334
newSchema : GraphQLSchema
337
- ) : Array < BreakingChange > {
335
+ ) : {
336
+ breakingChanges: Array < BreakingChange > ,
337
+ dangerousChanges : Array < DangerousChange >
338
+ } {
338
339
const oldTypeMap = oldSchema . getTypeMap ( ) ;
339
340
const newTypeMap = newSchema . getTypeMap ( ) ;
340
341
341
- const breakingFieldChanges = [ ] ;
342
+ const breakingChanges = [ ] ;
343
+ const dangerousChanges = [ ] ;
342
344
Object . keys ( oldTypeMap ) . forEach ( typeName => {
343
345
const oldType = oldTypeMap [ typeName ] ;
344
346
const newType = newTypeMap [ typeName ] ;
@@ -354,7 +356,7 @@ export function findFieldsThatChangedTypeOnInputObjectTypes(
354
356
Object . keys ( oldTypeFieldsDef ) . forEach ( fieldName => {
355
357
// Check if the field is missing on the type in the new schema.
356
358
if ( ! ( fieldName in newTypeFieldsDef ) ) {
357
- breakingFieldChanges . push ( {
359
+ breakingChanges . push ( {
358
360
type : BreakingChangeType . FIELD_REMOVED ,
359
361
description : `${ typeName } .${ fieldName } was removed.` ,
360
362
} ) ;
@@ -371,29 +373,37 @@ export function findFieldsThatChangedTypeOnInputObjectTypes(
371
373
const newFieldTypeString = isNamedType ( newFieldType ) ?
372
374
newFieldType . name :
373
375
newFieldType . toString ( ) ;
374
- breakingFieldChanges . push ( {
376
+ breakingChanges . push ( {
375
377
type : BreakingChangeType . FIELD_CHANGED_KIND ,
376
378
description : `${ typeName } .${ fieldName } changed type from ` +
377
379
`${ oldFieldTypeString } to ${ newFieldTypeString } .` ,
378
380
} ) ;
379
381
}
380
382
}
381
383
} ) ;
382
- // Check if a non-null field was added to the input object type
384
+ // Check if a field was added to the input object type
383
385
Object . keys ( newTypeFieldsDef ) . forEach ( fieldName => {
384
- if (
385
- ! ( fieldName in oldTypeFieldsDef ) &&
386
- newTypeFieldsDef [ fieldName ] . type instanceof GraphQLNonNull
387
- ) {
388
- breakingFieldChanges . push ( {
389
- type : BreakingChangeType . NON_NULL_INPUT_FIELD_ADDED ,
390
- description : `A non-null field ${ fieldName } on ` +
391
- `input type ${ newType . name } was added.` ,
392
- } ) ;
386
+ if ( ! ( fieldName in oldTypeFieldsDef ) ) {
387
+ if ( newTypeFieldsDef [ fieldName ] . type instanceof GraphQLNonNull ) {
388
+ breakingChanges . push ( {
389
+ type : BreakingChangeType . NON_NULL_INPUT_FIELD_ADDED ,
390
+ description : `A non-null field ${ fieldName } on ` +
391
+ `input type ${ newType . name } was added.` ,
392
+ } ) ;
393
+ } else {
394
+ dangerousChanges . push ( {
395
+ type : DangerousChangeType . NULLABLE_INPUT_FIELD_ADDED ,
396
+ description : `A nullable field ${ fieldName } on ` +
397
+ `input type ${ newType . name } was added.` ,
398
+ } ) ;
399
+ }
393
400
}
394
401
} ) ;
395
402
} ) ;
396
- return breakingFieldChanges ;
403
+ return {
404
+ breakingChanges,
405
+ dangerousChanges,
406
+ } ;
397
407
}
398
408
399
409
function isChangeSafeForObjectOrInterfaceField (
0 commit comments