Skip to content

Commit 2d22d32

Browse files
chore(core): add missing directive usage exports (graphql-hive#2906)
1 parent 8341829 commit 2d22d32

File tree

4 files changed

+181
-1
lines changed

4 files changed

+181
-1
lines changed

.changeset/chilled-squids-knock.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@graphql-inspector/core': minor
3+
---
4+
5+
add missing change detection of directive field usage and exports 'directiveUsageFieldAddedFromMeta' &
6+
'directiveUsageFieldRemovedFromMeta'

packages/core/__tests__/diff/directive-usage.test.ts

Lines changed: 106 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
import { buildSchema } from 'graphql';
2-
import { CriticalityLevel, diff } from '../../src/index.js';
2+
import {
3+
CriticalityLevel,
4+
diff,
5+
directiveUsageFieldAddedFromMeta,
6+
directiveUsageFieldRemovedFromMeta,
7+
} from '../../src/index.js';
38
import { findFirstChangeByPath } from '../../utils/testing.js';
49

510
describe('directive-usage', () => {
@@ -724,4 +729,104 @@ describe('directive-usage', () => {
724729
expect(change.message).toEqual("Directive 'external' was removed from schema 'Foo'");
725730
});
726731
});
732+
733+
describe('directiveUsageFieldAddedFromMeta and directiveUsageFieldRemovedFromMeta', () => {
734+
test('directiveUsageFieldAddedFromMeta creates correct change object', () => {
735+
const change = directiveUsageFieldAddedFromMeta({
736+
type: 'DIRECTIVE_USAGE_FIELD_ADDED',
737+
meta: {
738+
typeName: 'User',
739+
fieldName: 'email',
740+
addedDirectiveName: 'external',
741+
},
742+
});
743+
744+
expect(change.type).toEqual('DIRECTIVE_USAGE_FIELD_ADDED');
745+
expect(change.criticality.level).toEqual(CriticalityLevel.Dangerous);
746+
expect(change.criticality.reason).toEqual("Directive 'external' was added to field 'email'");
747+
expect(change.message).toEqual("Directive 'external' was added to field 'User.email'");
748+
expect(change.path).toEqual('User.email.external');
749+
expect(change.meta).toEqual({
750+
typeName: 'User',
751+
fieldName: 'email',
752+
addedDirectiveName: 'external',
753+
});
754+
});
755+
756+
test('directiveUsageFieldAddedFromMeta with @deprecated directive', () => {
757+
const change = directiveUsageFieldAddedFromMeta({
758+
type: 'DIRECTIVE_USAGE_FIELD_ADDED',
759+
meta: {
760+
typeName: 'User',
761+
fieldName: 'name',
762+
addedDirectiveName: 'deprecated',
763+
},
764+
});
765+
766+
expect(change.criticality.level).toEqual(CriticalityLevel.NonBreaking);
767+
});
768+
769+
test('directiveUsageFieldAddedFromMeta with @oneOf directive', () => {
770+
const change = directiveUsageFieldAddedFromMeta({
771+
type: 'DIRECTIVE_USAGE_FIELD_ADDED',
772+
meta: {
773+
typeName: 'SearchResult',
774+
fieldName: 'result',
775+
addedDirectiveName: 'oneOf',
776+
},
777+
});
778+
779+
expect(change.criticality.level).toEqual(CriticalityLevel.Breaking);
780+
});
781+
782+
test('directiveUsageFieldRemovedFromMeta creates correct change object', () => {
783+
const change = directiveUsageFieldRemovedFromMeta({
784+
type: 'DIRECTIVE_USAGE_FIELD_REMOVED',
785+
meta: {
786+
typeName: 'User',
787+
fieldName: 'email',
788+
removedDirectiveName: 'external',
789+
},
790+
});
791+
792+
expect(change.type).toEqual('DIRECTIVE_USAGE_FIELD_REMOVED');
793+
expect(change.criticality.level).toEqual(CriticalityLevel.Dangerous);
794+
expect(change.criticality.reason).toEqual(
795+
"Directive 'external' was removed from field 'email'",
796+
);
797+
expect(change.message).toEqual("Directive 'external' was removed from field 'User.email'");
798+
expect(change.path).toEqual('User.email.external');
799+
expect(change.meta).toEqual({
800+
typeName: 'User',
801+
fieldName: 'email',
802+
removedDirectiveName: 'external',
803+
});
804+
});
805+
806+
test('directiveUsageFieldRemovedFromMeta with @deprecated directive', () => {
807+
const change = directiveUsageFieldRemovedFromMeta({
808+
type: 'DIRECTIVE_USAGE_FIELD_REMOVED',
809+
meta: {
810+
typeName: 'User',
811+
fieldName: 'name',
812+
removedDirectiveName: 'deprecated',
813+
},
814+
});
815+
816+
expect(change.criticality.level).toEqual(CriticalityLevel.NonBreaking);
817+
});
818+
819+
test('directiveUsageFieldRemovedFromMeta with @oneOf directive', () => {
820+
const change = directiveUsageFieldRemovedFromMeta({
821+
type: 'DIRECTIVE_USAGE_FIELD_REMOVED',
822+
meta: {
823+
typeName: 'SearchResult',
824+
fieldName: 'result',
825+
removedDirectiveName: 'oneOf',
826+
},
827+
});
828+
829+
expect(change.criticality.level).toEqual(CriticalityLevel.NonBreaking);
830+
});
831+
});
727832
});

packages/core/src/diff/changes/directive-usage.ts

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,10 @@ import {
2323
DirectiveUsageEnumRemovedChange,
2424
DirectiveUsageEnumValueAddedChange,
2525
DirectiveUsageEnumValueRemovedChange,
26+
DirectiveUsageFieldAddedChange,
2627
DirectiveUsageFieldDefinitionAddedChange,
2728
DirectiveUsageFieldDefinitionRemovedChange,
29+
DirectiveUsageFieldRemovedChange,
2830
DirectiveUsageInputFieldDefinitionAddedChange,
2931
DirectiveUsageInputFieldDefinitionRemovedChange,
3032
DirectiveUsageInputObjectAddedChange,
@@ -72,6 +74,13 @@ type KindToPayload = {
7274
input: GraphQLEnumType;
7375
change: DirectiveUsageEnumAddedChange | DirectiveUsageEnumRemovedChange;
7476
};
77+
[Kind.FIELD]: {
78+
input: {
79+
field: GraphQLField<any, any, any>;
80+
parentType: GraphQLInterfaceType | GraphQLObjectType<any, any>;
81+
};
82+
change: DirectiveUsageFieldAddedChange | DirectiveUsageFieldRemovedChange;
83+
};
7584
[Kind.FIELD_DEFINITION]: {
7685
input: {
7786
field: GraphQLField<any, any, any>;
@@ -379,6 +388,25 @@ export function directiveUsageEnumRemovedFromMeta(args: DirectiveUsageEnumRemove
379388
} as const;
380389
}
381390

391+
function buildDirectiveUsageFieldAddedMessage(
392+
args: DirectiveUsageFieldAddedChange['meta'],
393+
): string {
394+
return `Directive '${args.addedDirectiveName}' was added to field '${args.typeName}.${args.fieldName}'`;
395+
}
396+
397+
export function directiveUsageFieldAddedFromMeta(args: DirectiveUsageFieldAddedChange) {
398+
return {
399+
criticality: {
400+
level: addedSpecialDirective(args.meta.addedDirectiveName, CriticalityLevel.Dangerous),
401+
reason: `Directive '${args.meta.addedDirectiveName}' was added to field '${args.meta.fieldName}'`,
402+
},
403+
type: ChangeType.DirectiveUsageFieldAdded,
404+
message: buildDirectiveUsageFieldAddedMessage(args.meta),
405+
path: [args.meta.typeName, args.meta.fieldName, args.meta.addedDirectiveName].join('.'),
406+
meta: args.meta,
407+
} as const;
408+
}
409+
382410
function buildDirectiveUsageFieldDefinitionAddedMessage(
383411
args: DirectiveUsageFieldDefinitionAddedChange['meta'],
384412
): string {
@@ -400,6 +428,25 @@ export function directiveUsageFieldDefinitionAddedFromMeta(
400428
} as const;
401429
}
402430

431+
function buildDirectiveUsageFieldRemovedMessage(
432+
args: DirectiveUsageFieldRemovedChange['meta'],
433+
): string {
434+
return `Directive '${args.removedDirectiveName}' was removed from field '${args.typeName}.${args.fieldName}'`;
435+
}
436+
437+
export function directiveUsageFieldRemovedFromMeta(args: DirectiveUsageFieldRemovedChange) {
438+
return {
439+
criticality: {
440+
level: removedSpecialDirective(args.meta.removedDirectiveName, CriticalityLevel.Dangerous),
441+
reason: `Directive '${args.meta.removedDirectiveName}' was removed from field '${args.meta.fieldName}'`,
442+
},
443+
type: ChangeType.DirectiveUsageFieldRemoved,
444+
message: buildDirectiveUsageFieldRemovedMessage(args.meta),
445+
path: [args.meta.typeName, args.meta.fieldName, args.meta.removedDirectiveName].join('.'),
446+
meta: args.meta,
447+
} as const;
448+
}
449+
403450
function buildDirectiveUsageFieldDefinitionRemovedMessage(
404451
args: DirectiveUsageFieldDefinitionRemovedChange['meta'],
405452
): string {
@@ -640,6 +687,16 @@ export function directiveUsageAdded<K extends keyof KindToPayload>(
640687
},
641688
});
642689
}
690+
if (isOfKind(kind, Kind.FIELD, payload)) {
691+
return directiveUsageFieldAddedFromMeta({
692+
type: ChangeType.DirectiveUsageFieldAdded,
693+
meta: {
694+
addedDirectiveName: directive.name.value,
695+
fieldName: payload.field.name,
696+
typeName: payload.parentType.name,
697+
},
698+
});
699+
}
643700
if (isOfKind(kind, Kind.FIELD_DEFINITION, payload)) {
644701
return directiveUsageFieldDefinitionAddedFromMeta({
645702
type: ChangeType.DirectiveUsageFieldDefinitionAdded,
@@ -757,6 +814,16 @@ export function directiveUsageRemoved<K extends keyof KindToPayload>(
757814
},
758815
});
759816
}
817+
if (isOfKind(kind, Kind.FIELD, payload)) {
818+
return directiveUsageFieldRemovedFromMeta({
819+
type: ChangeType.DirectiveUsageFieldRemoved,
820+
meta: {
821+
removedDirectiveName: directive.name.value,
822+
fieldName: payload.field.name,
823+
typeName: payload.parentType.name,
824+
},
825+
});
826+
}
760827
if (isOfKind(kind, Kind.FIELD_DEFINITION, payload)) {
761828
return directiveUsageFieldDefinitionRemovedFromMeta({
762829
type: ChangeType.DirectiveUsageFieldDefinitionRemoved,

packages/core/src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,11 @@ export {
3838
directiveUsageArgumentDefinitionRemovedFromMeta,
3939
directiveUsageEnumValueAddedFromMeta,
4040
directiveUsageEnumValueRemovedFromMeta,
41+
directiveUsageFieldAddedFromMeta,
4142
directiveUsageFieldDefinitionAddedFromMeta,
4243
directiveUsageAdded,
4344
directiveUsageFieldDefinitionRemovedFromMeta,
45+
directiveUsageFieldRemovedFromMeta,
4446
directiveUsageInputFieldDefinitionAddedFromMeta,
4547
directiveUsageInputFieldDefinitionRemovedFromMeta,
4648
directiveUsageInputObjectAddedFromMeta,

0 commit comments

Comments
 (0)