Skip to content

Commit 72c9044

Browse files
authoredFeb 12, 2025··
incremental: validate against @stream on different instances of the same field (#4342)
See discussion graphql/defer-stream-wg#100

File tree

2 files changed

+46
-27
lines changed

2 files changed

+46
-27
lines changed
 

‎src/validation/__tests__/OverlappingFieldsCanBeMergedRule-test.ts

+29-11
Original file line numberDiff line numberDiff line change
@@ -98,13 +98,22 @@ describe('Validate: Overlapping fields can be merged', () => {
9898
`);
9999
});
100100

101-
it('Same stream directives supported', () => {
102-
expectValid(`
101+
it('stream directive used on different instances of the same field', () => {
102+
expectErrors(`
103103
fragment differentDirectivesWithDifferentAliases on Dog {
104104
name @stream(label: "streamLabel", initialCount: 1)
105105
name @stream(label: "streamLabel", initialCount: 1)
106106
}
107-
`);
107+
`).toDeepEqual([
108+
{
109+
message:
110+
'Fields "name" conflict because they have overlapping stream directives. See https://github.com/graphql/defer-stream-wg/discussions/100. Use different aliases on the fields to fetch both if this was intentional.',
111+
locations: [
112+
{ line: 3, column: 9 },
113+
{ line: 4, column: 9 },
114+
],
115+
},
116+
]);
108117
});
109118

110119
it('different stream directive label', () => {
@@ -116,7 +125,7 @@ describe('Validate: Overlapping fields can be merged', () => {
116125
`).toDeepEqual([
117126
{
118127
message:
119-
'Fields "name" conflict because they have differing stream directives. Use different aliases on the fields to fetch both if this was intentional.',
128+
'Fields "name" conflict because they have overlapping stream directives. Use different aliases on the fields to fetch both if this was intentional.',
120129
locations: [
121130
{ line: 3, column: 9 },
122131
{ line: 4, column: 9 },
@@ -134,7 +143,7 @@ describe('Validate: Overlapping fields can be merged', () => {
134143
`).toDeepEqual([
135144
{
136145
message:
137-
'Fields "name" conflict because they have differing stream directives. Use different aliases on the fields to fetch both if this was intentional.',
146+
'Fields "name" conflict because they have overlapping stream directives. Use different aliases on the fields to fetch both if this was intentional.',
138147
locations: [
139148
{ line: 3, column: 9 },
140149
{ line: 4, column: 9 },
@@ -152,7 +161,7 @@ describe('Validate: Overlapping fields can be merged', () => {
152161
`).toDeepEqual([
153162
{
154163
message:
155-
'Fields "name" conflict because they have differing stream directives. Use different aliases on the fields to fetch both if this was intentional.',
164+
'Fields "name" conflict because they have overlapping stream directives. Use different aliases on the fields to fetch both if this was intentional.',
156165
locations: [
157166
{ line: 3, column: 9 },
158167
{ line: 4, column: 9 },
@@ -170,7 +179,7 @@ describe('Validate: Overlapping fields can be merged', () => {
170179
`).toDeepEqual([
171180
{
172181
message:
173-
'Fields "name" conflict because they have differing stream directives. Use different aliases on the fields to fetch both if this was intentional.',
182+
'Fields "name" conflict because they have overlapping stream directives. Use different aliases on the fields to fetch both if this was intentional.',
174183
locations: [
175184
{ line: 3, column: 9 },
176185
{ line: 4, column: 9 },
@@ -188,7 +197,7 @@ describe('Validate: Overlapping fields can be merged', () => {
188197
`).toDeepEqual([
189198
{
190199
message:
191-
'Fields "name" conflict because they have differing stream directives. Use different aliases on the fields to fetch both if this was intentional.',
200+
'Fields "name" conflict because they have overlapping stream directives. Use different aliases on the fields to fetch both if this was intentional.',
192201
locations: [
193202
{ line: 3, column: 9 },
194203
{ line: 4, column: 9 },
@@ -206,7 +215,7 @@ describe('Validate: Overlapping fields can be merged', () => {
206215
`).toDeepEqual([
207216
{
208217
message:
209-
'Fields "name" conflict because they have differing stream directives. Use different aliases on the fields to fetch both if this was intentional.',
218+
'Fields "name" conflict because they have overlapping stream directives. Use different aliases on the fields to fetch both if this was intentional.',
210219
locations: [
211220
{ line: 3, column: 9 },
212221
{ line: 4, column: 9 },
@@ -216,12 +225,21 @@ describe('Validate: Overlapping fields can be merged', () => {
216225
});
217226

218227
it('different stream directive both missing args', () => {
219-
expectValid(`
228+
expectErrors(`
220229
fragment conflictingArgs on Dog {
221230
name @stream
222231
name @stream
223232
}
224-
`);
233+
`).toDeepEqual([
234+
{
235+
message:
236+
'Fields "name" conflict because they have overlapping stream directives. See https://github.com/graphql/defer-stream-wg/discussions/100. Use different aliases on the fields to fetch both if this was intentional.',
237+
locations: [
238+
{ line: 3, column: 9 },
239+
{ line: 4, column: 9 },
240+
],
241+
},
242+
]);
225243
});
226244

227245
it('Same aliases with different field targets', () => {

‎src/validation/rules/OverlappingFieldsCanBeMergedRule.ts

+17-16
Original file line numberDiff line numberDiff line change
@@ -707,12 +707,14 @@ function findConflict(
707707

708708
const directives1 = node1.directives ?? [];
709709
const directives2 = node2.directives ?? [];
710-
if (!sameStreams(directives1, varMap1, directives2, varMap2)) {
711-
return [
712-
[responseName, 'they have differing stream directives'],
713-
[node1],
714-
[node2],
715-
];
710+
const overlappingStreamReason = hasNoOverlappingStreams(
711+
directives1,
712+
varMap1,
713+
directives2,
714+
varMap2,
715+
);
716+
if (overlappingStreamReason !== undefined) {
717+
return [[responseName, overlappingStreamReason], [node1], [node2]];
716718
}
717719

718720
// The return type for each field.
@@ -830,28 +832,27 @@ function getStreamDirective(
830832
return directives.find((directive) => directive.name.value === 'stream');
831833
}
832834

833-
function sameStreams(
835+
function hasNoOverlappingStreams(
834836
directives1: ReadonlyArray<DirectiveNode>,
835837
varMap1: Map<string, ValueNode> | undefined,
836838
directives2: ReadonlyArray<DirectiveNode>,
837839
varMap2: Map<string, ValueNode> | undefined,
838-
): boolean {
840+
): string | undefined {
839841
const stream1 = getStreamDirective(directives1);
840842
const stream2 = getStreamDirective(directives2);
841843
if (!stream1 && !stream2) {
842844
// both fields do not have streams
843-
return true;
845+
return;
844846
} else if (stream1 && stream2) {
845847
// check if both fields have equivalent streams
846-
return sameArguments(
847-
stream1.arguments,
848-
varMap1,
849-
stream2.arguments,
850-
varMap2,
851-
);
848+
if (sameArguments(stream1.arguments, varMap1, stream2.arguments, varMap2)) {
849+
// This was allowed in previous alpha versions of `graphql-js`.
850+
return 'they have overlapping stream directives. See https://github.com/graphql/defer-stream-wg/discussions/100';
851+
}
852+
return 'they have overlapping stream directives';
852853
}
853854
// fields have a mix of stream and no stream
854-
return false;
855+
return 'they have overlapping stream directives';
855856
}
856857

857858
// Two types conflict if both types could not apply to a value simultaneously.

0 commit comments

Comments
 (0)
Please sign in to comment.