Skip to content

Commit f236898

Browse files
kenleezleleebyron
authored andcommitted
Find breaking directive changes (#1152)
* find removed directives * added missing semi * set > array for lookups * find removed directive arguments * find added non-null directive arguments * find removed locations for a directive * detect removed locations from directives in a schema * mv findDangerous::new functions to findBreaking * s/Argument/Arg * minor refactor, introducing Schema::getDirectiveMap() * make detector changes live * lint: Arrow function should not return assignment * move getDirectiveMap to findBreakingChanges.js * moved getArgumentMap to findBreakingChanges.js * yayee for keyMap! * noop * noop
1 parent c4e301d commit f236898

File tree

2 files changed

+397
-0
lines changed

2 files changed

+397
-0
lines changed

src/utilities/__tests__/findBreakingChanges-test.js

Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,22 @@ import {
3636
findArgChanges,
3737
findInterfacesRemovedFromObjectTypes,
3838
findInterfacesAddedToObjectTypes,
39+
findRemovedDirectives,
40+
findRemovedDirectiveArgs,
41+
findAddedNonNullDirectiveArgs,
42+
findRemovedLocationsForDirective,
43+
findRemovedDirectiveLocations,
3944
} from '../findBreakingChanges';
4045

46+
import {
47+
GraphQLSkipDirective,
48+
GraphQLIncludeDirective,
49+
GraphQLDeprecatedDirective,
50+
GraphQLDirective,
51+
} from '../../type/directives';
52+
53+
import { DirectiveLocation } from '../../language/directiveLocation';
54+
4155
describe('findBreakingChanges', () => {
4256
const queryType = new GraphQLObjectType({
4357
name: 'Query',
@@ -1164,6 +1178,44 @@ describe('findBreakingChanges', () => {
11641178
},
11651179
});
11661180

1181+
const directiveThatIsRemoved = GraphQLSkipDirective;
1182+
const directiveThatRemovesArgOld = new GraphQLDirective({
1183+
name: 'DirectiveThatRemovesArg',
1184+
locations: [DirectiveLocation.FIELD_DEFINITION],
1185+
args: {
1186+
arg1: {
1187+
name: 'arg1',
1188+
},
1189+
},
1190+
});
1191+
const directiveThatRemovesArgNew = new GraphQLDirective({
1192+
name: 'DirectiveThatRemovesArg',
1193+
locations: [DirectiveLocation.FIELD_DEFINITION],
1194+
});
1195+
const nonNullDirectiveAddedOld = new GraphQLDirective({
1196+
name: 'NonNullDirectiveAdded',
1197+
locations: [DirectiveLocation.FIELD_DEFINITION],
1198+
});
1199+
const nonNullDirectiveAddedNew = new GraphQLDirective({
1200+
name: 'NonNullDirectiveAdded',
1201+
locations: [DirectiveLocation.FIELD_DEFINITION],
1202+
args: {
1203+
arg1: {
1204+
name: 'arg1',
1205+
type: GraphQLNonNull(GraphQLBoolean),
1206+
},
1207+
},
1208+
});
1209+
const directiveRemovedLocationOld = new GraphQLDirective({
1210+
name: 'Directive Name',
1211+
locations: [DirectiveLocation.FIELD_DEFINITION, DirectiveLocation.QUERY],
1212+
});
1213+
1214+
const directiveRemovedLocationNew = new GraphQLDirective({
1215+
name: 'Directive Name',
1216+
locations: [DirectiveLocation.FIELD_DEFINITION],
1217+
});
1218+
11671219
const oldSchema = new GraphQLSchema({
11681220
query: queryType,
11691221
types: [
@@ -1175,6 +1227,12 @@ describe('findBreakingChanges', () => {
11751227
argThatChanges,
11761228
typeThatLosesInterfaceOld,
11771229
],
1230+
directives: [
1231+
directiveThatIsRemoved,
1232+
directiveThatRemovesArgOld,
1233+
nonNullDirectiveAddedOld,
1234+
directiveRemovedLocationOld,
1235+
],
11781236
});
11791237

11801238
const newSchema = new GraphQLSchema({
@@ -1188,6 +1246,11 @@ describe('findBreakingChanges', () => {
11881246
typeThaLosesInterfaceNew,
11891247
interface1,
11901248
],
1249+
directives: [
1250+
directiveThatRemovesArgNew,
1251+
nonNullDirectiveAddedNew,
1252+
directiveRemovedLocationNew,
1253+
],
11911254
});
11921255

11931256
const expectedBreakingChanges = [
@@ -1242,11 +1305,172 @@ describe('findBreakingChanges', () => {
12421305
'TypeThatGainsInterface1 no longer implements ' +
12431306
'interface Interface1.',
12441307
},
1308+
{
1309+
type: BreakingChangeType.DIRECTIVE_REMOVED,
1310+
description: 'skip was removed',
1311+
},
1312+
{
1313+
type: BreakingChangeType.DIRECTIVE_ARG_REMOVED,
1314+
description: 'arg1 was removed from DirectiveThatRemovesArg',
1315+
},
1316+
{
1317+
type: BreakingChangeType.NON_NULL_DIRECTIVE_ARG_ADDED,
1318+
description:
1319+
'A non-null arg arg1 on directive ' +
1320+
'NonNullDirectiveAdded was added',
1321+
},
1322+
{
1323+
type: BreakingChangeType.DIRECTIVE_LOCATION_REMOVED,
1324+
description: 'QUERY was removed from Directive Name',
1325+
},
12451326
];
12461327
expect(findBreakingChanges(oldSchema, newSchema)).to.eql(
12471328
expectedBreakingChanges,
12481329
);
12491330
});
1331+
1332+
it('should detect if a directive was explicitly removed', () => {
1333+
const oldSchema = new GraphQLSchema({
1334+
directives: [GraphQLSkipDirective, GraphQLIncludeDirective],
1335+
});
1336+
1337+
const newSchema = new GraphQLSchema({
1338+
directives: [GraphQLSkipDirective],
1339+
});
1340+
1341+
expect(findRemovedDirectives(oldSchema, newSchema)).to.eql([
1342+
{
1343+
type: BreakingChangeType.DIRECTIVE_REMOVED,
1344+
description: `${GraphQLIncludeDirective.name} was removed`,
1345+
},
1346+
]);
1347+
});
1348+
1349+
it('should detect if a directive was implicitly removed', () => {
1350+
const oldSchema = new GraphQLSchema({});
1351+
1352+
const newSchema = new GraphQLSchema({
1353+
directives: [GraphQLSkipDirective, GraphQLIncludeDirective],
1354+
});
1355+
1356+
expect(findRemovedDirectives(oldSchema, newSchema)).to.eql([
1357+
{
1358+
type: BreakingChangeType.DIRECTIVE_REMOVED,
1359+
description: `${GraphQLDeprecatedDirective.name} was removed`,
1360+
},
1361+
]);
1362+
});
1363+
1364+
it('should detect if a directive argument was removed', () => {
1365+
const oldSchema = new GraphQLSchema({
1366+
directives: [
1367+
new GraphQLDirective({
1368+
name: 'DirectiveWithArg',
1369+
locations: [DirectiveLocation.FIELD_DEFINITION],
1370+
args: {
1371+
arg1: {
1372+
name: 'arg1',
1373+
},
1374+
},
1375+
}),
1376+
],
1377+
});
1378+
1379+
const newSchema = new GraphQLSchema({
1380+
directives: [
1381+
new GraphQLDirective({
1382+
name: 'DirectiveWithArg',
1383+
locations: [DirectiveLocation.FIELD_DEFINITION],
1384+
}),
1385+
],
1386+
});
1387+
1388+
expect(findRemovedDirectiveArgs(oldSchema, newSchema)).to.eql([
1389+
{
1390+
type: BreakingChangeType.DIRECTIVE_ARG_REMOVED,
1391+
description: 'arg1 was removed from DirectiveWithArg',
1392+
},
1393+
]);
1394+
});
1395+
1396+
it('should detect if a non-nullable directive argument was added', () => {
1397+
const oldSchema = new GraphQLSchema({
1398+
directives: [
1399+
new GraphQLDirective({
1400+
name: 'DirectiveName',
1401+
locations: [DirectiveLocation.FIELD_DEFINITION],
1402+
}),
1403+
],
1404+
});
1405+
1406+
const newSchema = new GraphQLSchema({
1407+
directives: [
1408+
new GraphQLDirective({
1409+
name: 'DirectiveName',
1410+
locations: [DirectiveLocation.FIELD_DEFINITION],
1411+
args: {
1412+
arg1: {
1413+
name: 'arg1',
1414+
type: GraphQLNonNull(GraphQLBoolean),
1415+
},
1416+
},
1417+
}),
1418+
],
1419+
});
1420+
1421+
expect(findAddedNonNullDirectiveArgs(oldSchema, newSchema)).to.eql([
1422+
{
1423+
type: BreakingChangeType.NON_NULL_DIRECTIVE_ARG_ADDED,
1424+
description: 'A non-null arg arg1 on directive DirectiveName was added',
1425+
},
1426+
]);
1427+
});
1428+
1429+
it('should detect locations removed from a directive', () => {
1430+
const d1 = new GraphQLDirective({
1431+
name: 'Directive Name',
1432+
locations: [DirectiveLocation.FIELD_DEFINITION, DirectiveLocation.QUERY],
1433+
});
1434+
1435+
const d2 = new GraphQLDirective({
1436+
name: 'Directive Name',
1437+
locations: [DirectiveLocation.FIELD_DEFINITION],
1438+
});
1439+
1440+
expect(findRemovedLocationsForDirective(d1, d2)).to.eql([
1441+
DirectiveLocation.QUERY,
1442+
]);
1443+
});
1444+
1445+
it('should detect locations removed directives within a schema', () => {
1446+
const oldSchema = new GraphQLSchema({
1447+
directives: [
1448+
new GraphQLDirective({
1449+
name: 'Directive Name',
1450+
locations: [
1451+
DirectiveLocation.FIELD_DEFINITION,
1452+
DirectiveLocation.QUERY,
1453+
],
1454+
}),
1455+
],
1456+
});
1457+
1458+
const newSchema = new GraphQLSchema({
1459+
directives: [
1460+
new GraphQLDirective({
1461+
name: 'Directive Name',
1462+
locations: [DirectiveLocation.FIELD_DEFINITION],
1463+
}),
1464+
],
1465+
});
1466+
1467+
expect(findRemovedDirectiveLocations(oldSchema, newSchema)).to.eql([
1468+
{
1469+
type: BreakingChangeType.DIRECTIVE_LOCATION_REMOVED,
1470+
description: 'QUERY was removed from Directive Name',
1471+
},
1472+
]);
1473+
});
12501474
});
12511475

12521476
describe('findDangerousChanges', () => {

0 commit comments

Comments
 (0)