@apollo/federation-internals 2.0.0-preview.7 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. package/CHANGELOG.md +32 -3
  2. package/dist/buildSchema.d.ts.map +1 -1
  3. package/dist/buildSchema.js +51 -41
  4. package/dist/buildSchema.js.map +1 -1
  5. package/dist/coreSpec.d.ts +16 -8
  6. package/dist/coreSpec.d.ts.map +1 -1
  7. package/dist/coreSpec.js +205 -53
  8. package/dist/coreSpec.js.map +1 -1
  9. package/dist/definitions.d.ts +28 -11
  10. package/dist/definitions.d.ts.map +1 -1
  11. package/dist/definitions.js +185 -67
  12. package/dist/definitions.js.map +1 -1
  13. package/dist/directiveAndTypeSpecification.d.ts +11 -1
  14. package/dist/directiveAndTypeSpecification.d.ts.map +1 -1
  15. package/dist/directiveAndTypeSpecification.js +77 -20
  16. package/dist/directiveAndTypeSpecification.js.map +1 -1
  17. package/dist/error.d.ts +17 -0
  18. package/dist/error.d.ts.map +1 -1
  19. package/dist/error.js +54 -2
  20. package/dist/error.js.map +1 -1
  21. package/dist/extractSubgraphsFromSupergraph.d.ts.map +1 -1
  22. package/dist/extractSubgraphsFromSupergraph.js +7 -1
  23. package/dist/extractSubgraphsFromSupergraph.js.map +1 -1
  24. package/dist/federation.d.ts +22 -5
  25. package/dist/federation.d.ts.map +1 -1
  26. package/dist/federation.js +143 -86
  27. package/dist/federation.js.map +1 -1
  28. package/dist/federationSpec.d.ts +6 -2
  29. package/dist/federationSpec.d.ts.map +1 -1
  30. package/dist/federationSpec.js +47 -22
  31. package/dist/federationSpec.js.map +1 -1
  32. package/dist/inaccessibleSpec.d.ts +10 -2
  33. package/dist/inaccessibleSpec.d.ts.map +1 -1
  34. package/dist/inaccessibleSpec.js +634 -16
  35. package/dist/inaccessibleSpec.js.map +1 -1
  36. package/dist/index.d.ts +2 -0
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.js +2 -0
  39. package/dist/index.js.map +1 -1
  40. package/dist/introspection.d.ts.map +1 -1
  41. package/dist/introspection.js +8 -3
  42. package/dist/introspection.js.map +1 -1
  43. package/dist/joinSpec.d.ts +5 -1
  44. package/dist/joinSpec.d.ts.map +1 -1
  45. package/dist/joinSpec.js +21 -0
  46. package/dist/joinSpec.js.map +1 -1
  47. package/dist/knownCoreFeatures.d.ts +4 -0
  48. package/dist/knownCoreFeatures.d.ts.map +1 -0
  49. package/dist/knownCoreFeatures.js +16 -0
  50. package/dist/knownCoreFeatures.js.map +1 -0
  51. package/dist/operations.d.ts +1 -0
  52. package/dist/operations.d.ts.map +1 -1
  53. package/dist/operations.js +16 -1
  54. package/dist/operations.js.map +1 -1
  55. package/dist/{sharing.d.ts → precompute.d.ts} +1 -1
  56. package/dist/precompute.d.ts.map +1 -0
  57. package/dist/{sharing.js → precompute.js} +3 -3
  58. package/dist/precompute.js.map +1 -0
  59. package/dist/schemaUpgrader.d.ts.map +1 -1
  60. package/dist/schemaUpgrader.js +17 -7
  61. package/dist/schemaUpgrader.js.map +1 -1
  62. package/dist/suggestions.d.ts +1 -1
  63. package/dist/suggestions.d.ts.map +1 -1
  64. package/dist/suggestions.js.map +1 -1
  65. package/dist/supergraphs.d.ts.map +1 -1
  66. package/dist/supergraphs.js +2 -0
  67. package/dist/supergraphs.js.map +1 -1
  68. package/dist/tagSpec.d.ts +7 -2
  69. package/dist/tagSpec.d.ts.map +1 -1
  70. package/dist/tagSpec.js +35 -14
  71. package/dist/tagSpec.js.map +1 -1
  72. package/dist/validate.js +13 -7
  73. package/dist/validate.js.map +1 -1
  74. package/dist/values.d.ts +2 -2
  75. package/dist/values.d.ts.map +1 -1
  76. package/dist/values.js +13 -11
  77. package/dist/values.js.map +1 -1
  78. package/package.json +4 -4
  79. package/src/__tests__/coreSpec.test.ts +212 -0
  80. package/src/__tests__/definitions.test.ts +75 -0
  81. package/src/__tests__/removeInaccessibleElements.test.ts +2229 -137
  82. package/src/__tests__/schemaUpgrader.test.ts +3 -2
  83. package/src/__tests__/subgraphValidation.test.ts +419 -4
  84. package/src/__tests__/values.test.ts +315 -3
  85. package/src/buildSchema.ts +98 -51
  86. package/src/coreSpec.ts +277 -65
  87. package/src/definitions.ts +317 -92
  88. package/src/directiveAndTypeSpecification.ts +98 -21
  89. package/src/error.ts +119 -1
  90. package/src/extractSubgraphsFromSupergraph.ts +7 -1
  91. package/src/federation.ts +184 -102
  92. package/src/federationSpec.ts +56 -24
  93. package/src/inaccessibleSpec.ts +985 -39
  94. package/src/index.ts +2 -0
  95. package/src/introspection.ts +8 -3
  96. package/src/joinSpec.ts +33 -3
  97. package/src/knownCoreFeatures.ts +13 -0
  98. package/src/operations.ts +15 -0
  99. package/src/{sharing.ts → precompute.ts} +3 -6
  100. package/src/schemaUpgrader.ts +29 -13
  101. package/src/suggestions.ts +1 -1
  102. package/src/supergraphs.ts +2 -0
  103. package/src/tagSpec.ts +49 -16
  104. package/src/validate.ts +20 -9
  105. package/src/values.ts +39 -12
  106. package/tsconfig.test.tsbuildinfo +1 -1
  107. package/tsconfig.tsbuildinfo +1 -1
  108. package/dist/sharing.d.ts.map +0 -1
  109. package/dist/sharing.js.map +0 -1
@@ -1,79 +1,124 @@
1
- import { ObjectType } from '../definitions';
2
- import { buildSchema } from '../buildSchema';
3
- import { removeInaccessibleElements } from '../inaccessibleSpec';
4
-
5
- describe('removeInaccessibleElements', () => {
6
- it(`removes @inaccessible fields`, () => {
1
+ import {
2
+ ArgumentDefinition,
3
+ errorCauses,
4
+ FieldDefinition,
5
+ InterfaceType,
6
+ ObjectType,
7
+ UnionType,
8
+ } from "../definitions";
9
+ import { buildSchema } from "../buildSchema";
10
+ import { removeInaccessibleElements } from "../inaccessibleSpec";
11
+ import { GraphQLErrorExt } from "@apollo/core-schema/dist/error";
12
+ import { GraphQLError } from "graphql";
13
+
14
+ describe("removeInaccessibleElements", () => {
15
+ const INACCESSIBLE_V02_HEADER = `
16
+ directive @core(feature: String!, as: String, for: core__Purpose) repeatable on SCHEMA
17
+
18
+ enum core__Purpose {
19
+ EXECUTION
20
+ SECURITY
21
+ }
22
+
23
+ directive @inaccessible on FIELD_DEFINITION | OBJECT | INTERFACE | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION
24
+
25
+ schema
26
+ @core(feature: "https://specs.apollo.dev/core/v0.2")
27
+ @core(feature: "https://specs.apollo.dev/inaccessible/v0.2")
28
+ {
29
+ query: Query
30
+ }
31
+ `;
32
+
33
+ function getCauses(e: unknown): GraphQLError[] {
34
+ expect(e instanceof GraphQLErrorExt).toBeTruthy();
35
+ const causes = errorCauses(e as Error);
36
+ expect(causes).toBeDefined();
37
+ expect(Array.isArray(causes)).toBeTruthy();
38
+ for (const cause of causes!) {
39
+ expect(cause instanceof GraphQLError).toBeTruthy();
40
+ }
41
+ return causes!;
42
+ }
43
+
44
+ function expectErrors(expectedCauseCount: number, f: () => void): string[] {
45
+ let error: unknown = undefined;
46
+ try {
47
+ f();
48
+ } catch (e) {
49
+ error = e;
50
+ }
51
+
52
+ expect(error).toBeDefined();
53
+ const causes = getCauses(error);
54
+ expect(causes).toHaveLength(expectedCauseCount);
55
+ const messages = causes.map((cause) => cause.message);
56
+ for (const message of messages) {
57
+ expect(typeof message === "string").toBeTruthy();
58
+ }
59
+ messages.sort();
60
+ return messages;
61
+ }
62
+
63
+ it(`succeeds for no inaccessible spec`, () => {
7
64
  const schema = buildSchema(`
8
65
  directive @core(feature: String!, as: String, for: core__Purpose) repeatable on SCHEMA
9
66
 
10
- directive @inaccessible on FIELD_DEFINITION | OBJECT | INTERFACE | UNION
67
+ enum core__Purpose {
68
+ EXECUTION
69
+ SECURITY
70
+ }
11
71
 
12
72
  schema
13
73
  @core(feature: "https://specs.apollo.dev/core/v0.2")
14
- @core(feature: "https://specs.apollo.dev/inaccessible/v0.1")
15
74
  {
16
75
  query: Query
17
76
  }
18
77
 
19
- enum core__Purpose {
20
- EXECUTION
21
- SECURITY
22
- }
23
-
24
78
  type Query {
25
79
  someField: String
26
- privateField: String @inaccessible
27
80
  }
28
81
  `);
29
82
 
30
83
  removeInaccessibleElements(schema);
31
-
32
- const queryType = schema.schemaDefinition.rootType('query')!;
33
-
34
- expect(queryType.field('someField')).toBeDefined();
35
- expect(queryType.field('privateField')).toBeUndefined();
84
+ schema.validate();
36
85
  });
37
86
 
38
- it(`removes @inaccessible object types`, () => {
87
+ it(`doesn't affect non-core @inaccessible`, () => {
39
88
  const schema = buildSchema(`
40
89
  directive @core(feature: String!, as: String, for: core__Purpose) repeatable on SCHEMA
41
90
 
42
- directive @inaccessible on FIELD_DEFINITION | OBJECT | INTERFACE | UNION
91
+ enum core__Purpose {
92
+ EXECUTION
93
+ SECURITY
94
+ }
43
95
 
44
96
  schema
45
97
  @core(feature: "https://specs.apollo.dev/core/v0.2")
46
- @core(feature: "https://specs.apollo.dev/inaccessible/v0.1")
47
98
  {
48
99
  query: Query
49
100
  }
50
101
 
51
- enum core__Purpose {
52
- EXECUTION
53
- SECURITY
54
- }
55
-
56
102
  type Query {
57
- fooField: Foo @inaccessible
58
- }
59
-
60
- type Foo @inaccessible {
61
- someField: String
103
+ someField: String @inaccessible
62
104
  }
63
105
 
64
- union Bar = Foo
106
+ directive @inaccessible on FIELD_DEFINITION | OBJECT | INTERFACE | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION
65
107
  `);
66
108
 
67
109
  removeInaccessibleElements(schema);
68
-
69
- expect(schema.type('Foo')).toBeUndefined();
110
+ schema.validate();
111
+ expect(schema.elementByCoordinate("Query.someField")).toBeDefined();
70
112
  });
71
113
 
72
- it(`removes @inaccessible interface types`, () => {
114
+ it(`fails for no @inaccessible definition`, () => {
73
115
  const schema = buildSchema(`
74
116
  directive @core(feature: String!, as: String, for: core__Purpose) repeatable on SCHEMA
75
117
 
76
- directive @inaccessible on FIELD_DEFINITION | OBJECT | INTERFACE | UNION
118
+ enum core__Purpose {
119
+ EXECUTION
120
+ SECURITY
121
+ }
77
122
 
78
123
  schema
79
124
  @core(feature: "https://specs.apollo.dev/core/v0.2")
@@ -82,217 +127,2264 @@ describe('removeInaccessibleElements', () => {
82
127
  query: Query
83
128
  }
84
129
 
130
+ type Query {
131
+ someField: String
132
+ }
133
+ `);
134
+
135
+ const errorMessages = expectErrors(1, () => {
136
+ removeInaccessibleElements(schema);
137
+ });
138
+
139
+ expect(errorMessages).toMatchInlineSnapshot(`
140
+ Array [
141
+ "Invalid schema: declares https://specs.apollo.dev/inaccessible/v0.1 spec but does not define a @inaccessible directive.",
142
+ ]
143
+ `);
144
+ });
145
+
146
+ it(`fails for incompatible @inaccessible definition`, () => {
147
+ const schema = buildSchema(`
148
+ directive @core(feature: String!, as: String, for: core__Purpose) repeatable on SCHEMA
149
+
85
150
  enum core__Purpose {
86
151
  EXECUTION
87
152
  SECURITY
88
153
  }
89
154
 
90
- type Query {
91
- fooField: Foo @inaccessible
92
- }
155
+ directive @inaccessible on FIELD_DEFINITION | OBJECT | INTERFACE | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION
93
156
 
94
- interface Foo @inaccessible {
95
- someField: String
157
+ schema
158
+ @core(feature: "https://specs.apollo.dev/core/v0.2")
159
+ @core(feature: "https://specs.apollo.dev/inaccessible/v0.1")
160
+ {
161
+ query: Query
96
162
  }
97
163
 
98
- type Bar implements Foo {
164
+ type Query {
99
165
  someField: String
100
166
  }
101
167
  `);
102
168
 
103
- removeInaccessibleElements(schema);
169
+ const errorMessages = expectErrors(1, () => {
170
+ removeInaccessibleElements(schema);
171
+ });
104
172
 
105
- expect(schema.type('Foo')).toBeUndefined();
106
- const barType = schema.type('Bar') as ObjectType | undefined;
107
- expect(barType).toBeDefined();
108
- expect(barType?.field('someField')).toBeDefined();
109
- expect([...barType!.interfaces()]).toHaveLength(0);
173
+ expect(errorMessages).toMatchInlineSnapshot(`
174
+ Array [
175
+ "Found invalid @inaccessible directive definition. Please ensure the directive definition in your schema's definitions matches the following:
176
+ directive @inaccessible on FIELD_DEFINITION | INTERFACE | OBJECT | UNION",
177
+ ]
178
+ `);
110
179
  });
111
180
 
112
- it(`removes @inaccessible union types`, () => {
181
+ it(`handles renames of @inaccessible`, () => {
113
182
  const schema = buildSchema(`
114
183
  directive @core(feature: String!, as: String, for: core__Purpose) repeatable on SCHEMA
115
184
 
116
- directive @inaccessible on FIELD_DEFINITION | OBJECT | INTERFACE | UNION
185
+ enum core__Purpose {
186
+ EXECUTION
187
+ SECURITY
188
+ }
189
+
190
+ directive @foo on FIELD_DEFINITION | OBJECT | INTERFACE | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION
117
191
 
118
192
  schema
119
193
  @core(feature: "https://specs.apollo.dev/core/v0.2")
120
- @core(feature: "https://specs.apollo.dev/inaccessible/v0.1")
194
+ @core(feature: "https://specs.apollo.dev/inaccessible/v0.2", as: "foo")
121
195
  {
122
196
  query: Query
123
197
  }
124
198
 
125
- enum core__Purpose {
126
- EXECUTION
127
- SECURITY
128
- }
129
-
130
199
  type Query {
131
- fooField: Foo @inaccessible
200
+ someField: Bar @inaccessible
201
+ privateField: String @foo
132
202
  }
133
203
 
134
- union Foo @inaccessible = Bar | Baz
204
+ scalar Bar
205
+
206
+ directive @inaccessible on FIELD_DEFINITION | OBJECT | INTERFACE | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION
207
+ `);
208
+
209
+ removeInaccessibleElements(schema);
210
+ schema.validate();
211
+ expect(schema.elementByCoordinate("Query.someField")).toBeDefined();
212
+ expect(schema.elementByCoordinate("Query.privateField")).toBeUndefined();
213
+ });
135
214
 
136
- type Bar {
215
+ it(`fails for @inaccessible built-ins`, () => {
216
+ const schema = buildSchema(`
217
+ ${INACCESSIBLE_V02_HEADER}
218
+
219
+ type Query {
137
220
  someField: String
138
221
  }
139
222
 
140
- type Baz {
141
- anotherField: String
142
- }
223
+ # Built-in scalar
224
+ scalar String @inaccessible
225
+
226
+ # Built-in directive
227
+ directive @deprecated(
228
+ reason: String = "No longer supported" @inaccessible
229
+ ) on FIELD_DEFINITION | ENUM_VALUE
143
230
  `);
144
231
 
145
- removeInaccessibleElements(schema);
232
+ const errorMessages = expectErrors(2, () => {
233
+ removeInaccessibleElements(schema);
234
+ });
146
235
 
147
- expect(schema.type('Foo')).toBeUndefined();
148
- expect(schema.type('Bar')).toBeDefined();
149
- expect(schema.type('Baz')).toBeDefined();
236
+ expect(errorMessages).toMatchInlineSnapshot(`
237
+ Array [
238
+ "Built-in directive \\"@deprecated\\" cannot use @inaccessible.",
239
+ "Built-in type \\"String\\" cannot use @inaccessible.",
240
+ ]
241
+ `);
150
242
  });
151
243
 
152
- it(`throws when a field returning an @inaccessible type isn't marked @inaccessible itself`, () => {
244
+ it(`fails for @inaccessible core feature definitions`, () => {
153
245
  const schema = buildSchema(`
154
- directive @core(feature: String!, as: String, for: core__Purpose) repeatable on SCHEMA
246
+ directive @core(feature: String! @inaccessible, as: String, for: core__Purpose) repeatable on SCHEMA
247
+
248
+ enum core__Purpose {
249
+ EXECUTION @inaccessible
250
+ SECURITY
251
+ }
155
252
 
156
- directive @inaccessible on FIELD_DEFINITION | OBJECT | INTERFACE | UNION
253
+ directive @inaccessible on FIELD_DEFINITION | OBJECT | INTERFACE | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION
157
254
 
158
255
  schema
159
256
  @core(feature: "https://specs.apollo.dev/core/v0.2")
160
- @core(feature: "https://specs.apollo.dev/inaccessible/v0.1")
257
+ @core(feature: "https://specs.apollo.dev/inaccessible/v0.2")
258
+ @core(feature: "http://localhost/foo/v1.0")
161
259
  {
162
260
  query: Query
163
261
  }
164
262
 
165
- enum core__Purpose {
166
- EXECUTION
167
- SECURITY
263
+ type Query {
264
+ someField: String!
168
265
  }
169
266
 
170
- type Query {
171
- fooField: Foo
267
+ # Object type
268
+ type foo__Object1 @inaccessible {
269
+ foo__Field: String!
270
+ }
271
+
272
+ # Object field
273
+ type foo__Object2 implements foo__Interface2 {
274
+ foo__Field: foo__Enum1! @inaccessible
275
+ }
276
+
277
+ # Object field argument
278
+ type foo__Object3 {
279
+ someField(someArg: foo__Enum1 @inaccessible): foo__Enum2!
280
+ }
281
+
282
+ # Interface type
283
+ interface foo__Interface1 @inaccessible {
284
+ foo__Field: String!
285
+ }
286
+
287
+ # Interface field
288
+ interface foo__Interface2 {
289
+ foo__Field: foo__Enum1! @inaccessible
290
+ }
291
+
292
+ # Interface field argument
293
+ interface foo__Interface3 {
294
+ someField(someArg: foo__InputObject1 @inaccessible): foo__Enum2!
295
+ }
296
+
297
+ # Union type
298
+ union foo__Union @inaccessible = foo__Object1 | foo__Object2 | foo__Object3
299
+
300
+ # Input object type
301
+ input foo__InputObject1 @inaccessible {
302
+ someField: foo__Enum1
303
+ }
304
+
305
+ # Input object field
306
+ input foo__InputObject2 {
307
+ someField: foo__Scalar @inaccessible
308
+ }
309
+
310
+ # Enum type
311
+ enum foo__Enum1 @inaccessible {
312
+ someValue
172
313
  }
173
314
 
174
- type Foo @inaccessible {
315
+ # Enum value
316
+ enum foo__Enum2 {
317
+ someValue @inaccessible
318
+ }
319
+
320
+ # Scalar type
321
+ scalar foo__Scalar @inaccessible
322
+
323
+ # Directive argument
324
+ directive @foo(arg: foo__InputObject2 @inaccessible) repeatable on OBJECT
325
+ `);
326
+
327
+ // 15 = 6 type kinds + 3 field kinds + 3 argument kinds + 1 enum value + 2 extras on special core elements
328
+ const errorMessages = expectErrors(15, () => {
329
+ removeInaccessibleElements(schema);
330
+ });
331
+
332
+ expect(errorMessages).toMatchInlineSnapshot(`
333
+ Array [
334
+ "Core feature directive \\"@core\\" cannot use @inaccessible.",
335
+ "Core feature directive \\"@foo\\" cannot use @inaccessible.",
336
+ "Core feature type \\"core__Purpose\\" cannot use @inaccessible.",
337
+ "Core feature type \\"foo__Enum1\\" cannot use @inaccessible.",
338
+ "Core feature type \\"foo__Enum2\\" cannot use @inaccessible.",
339
+ "Core feature type \\"foo__InputObject1\\" cannot use @inaccessible.",
340
+ "Core feature type \\"foo__InputObject2\\" cannot use @inaccessible.",
341
+ "Core feature type \\"foo__Interface1\\" cannot use @inaccessible.",
342
+ "Core feature type \\"foo__Interface2\\" cannot use @inaccessible.",
343
+ "Core feature type \\"foo__Interface3\\" cannot use @inaccessible.",
344
+ "Core feature type \\"foo__Object1\\" cannot use @inaccessible.",
345
+ "Core feature type \\"foo__Object2\\" cannot use @inaccessible.",
346
+ "Core feature type \\"foo__Object3\\" cannot use @inaccessible.",
347
+ "Core feature type \\"foo__Scalar\\" cannot use @inaccessible.",
348
+ "Core feature type \\"foo__Union\\" cannot use @inaccessible.",
349
+ ]
350
+ `);
351
+ });
352
+
353
+ it(`fails for @inaccessible directive definitions that aren't only executable`, () => {
354
+ const schema = buildSchema(`
355
+ ${INACCESSIBLE_V02_HEADER}
356
+
357
+ type Query {
175
358
  someField: String
176
359
  }
177
360
 
178
- union Bar = Foo
361
+ directive @foo(arg1: String @inaccessible) repeatable on OBJECT
362
+
363
+ directive @bar(arg2: String, arg3: String @inaccessible) repeatable on SCHEMA | FIELD
179
364
  `);
180
365
 
181
- expect(() => {
366
+ const errorMessages = expectErrors(2, () => {
182
367
  removeInaccessibleElements(schema);
183
- }).toThrow(
184
- `Field Query.fooField returns an @inaccessible type without being marked @inaccessible itself`,
185
- );
368
+ });
369
+
370
+ expect(errorMessages).toMatchInlineSnapshot(`
371
+ Array [
372
+ "Directive \\"@bar\\" cannot use @inaccessible because it may be applied to these type-system locations: SCHEMA.",
373
+ "Directive \\"@foo\\" cannot use @inaccessible because it may be applied to these type-system locations: OBJECT.",
374
+ ]
375
+ `);
186
376
  });
187
377
 
188
- it(`removes @inaccessible query root type`, () => {
378
+ it(`removes @inaccessible object types`, () => {
189
379
  const schema = buildSchema(`
190
- directive @core(feature: String!, as: String, for: core__Purpose) repeatable on SCHEMA
380
+ ${INACCESSIBLE_V02_HEADER}
191
381
 
192
- directive @inaccessible on FIELD_DEFINITION | OBJECT | INTERFACE | UNION
382
+ extend schema {
383
+ mutation: Mutation
384
+ subscription: Subscription
385
+ }
193
386
 
194
- schema
195
- @core(feature: "https://specs.apollo.dev/core/v0.2")
196
- @core(feature: "https://specs.apollo.dev/inaccessible/v0.1")
197
- {
198
- query: Query
387
+ # Non-inaccessible object type
388
+ type Query {
389
+ someField: String
199
390
  }
200
391
 
201
- enum core__Purpose {
202
- EXECUTION
203
- SECURITY
392
+ # Inaccessible mutation types should be removed
393
+ type Mutation @inaccessible {
394
+ someObject: Object
204
395
  }
205
396
 
206
- type Query @inaccessible {
207
- fooField: Foo
397
+ # Inaccessible subscription types should be removed
398
+ type Subscription @inaccessible {
399
+ someField: String
400
+ }
401
+
402
+ # Inaccessible object type
403
+ type Object @inaccessible {
404
+ someField: String
405
+ }
406
+
407
+ # Inaccessible object type referenced by inaccessible object field
408
+ type Referencer1 implements Referencer3 {
409
+ someField: String
410
+ privatefield: Object! @inaccessible
411
+ }
412
+
413
+ # Inaccessible object type referenced by non-inaccessible object field
414
+ # with inaccessible parent
415
+ type Referencer2 implements Referencer4 @inaccessible {
416
+ privateField: [Object!]!
208
417
  }
209
418
 
210
- type Foo {
419
+ # Inaccessible object type referenced by inaccessible interface field
420
+ interface Referencer3 {
211
421
  someField: String
422
+ privatefield: Object @inaccessible
423
+ }
424
+
425
+ # Inaccessible object type referenced by non-inaccessible interface field
426
+ # with inaccessible parent
427
+ interface Referencer4 @inaccessible {
428
+ privateField: [Object]
212
429
  }
430
+
431
+ # Inaccessible object type referenced by union member with
432
+ # non-inaccessible siblings and parent
433
+ union Referencer5 = Query | Object
434
+
435
+ # Inaccessible object type referenced by union member with no siblings
436
+ # but with inaccessible parent
437
+ union Referencer6 @inaccessible = Object
213
438
  `);
214
439
 
215
440
  removeInaccessibleElements(schema);
441
+ schema.validate();
442
+ expect(schema.elementByCoordinate("Query")).toBeDefined();
443
+ expect(schema.elementByCoordinate("Mutation")).toBeUndefined();
444
+ expect(schema.elementByCoordinate("Subscription")).toBeUndefined();
445
+ expect(schema.elementByCoordinate("Object")).toBeUndefined();
446
+ expect(schema.elementByCoordinate("Referencer1.someField")).toBeDefined();
447
+ expect(
448
+ schema.elementByCoordinate("Referencer1.privatefield")
449
+ ).toBeUndefined();
450
+ expect(schema.elementByCoordinate("Referencer2")).toBeUndefined();
451
+ expect(schema.elementByCoordinate("Referencer3.someField")).toBeDefined();
452
+ expect(
453
+ schema.elementByCoordinate("Referencer3.privatefield")
454
+ ).toBeUndefined();
455
+ expect(schema.elementByCoordinate("Referencer4")).toBeUndefined();
456
+ const unionType = schema.elementByCoordinate("Referencer5");
457
+ expect(unionType instanceof UnionType).toBeTruthy();
458
+ expect((unionType as UnionType).hasTypeMember("Query")).toBeTruthy();
459
+ expect((unionType as UnionType).hasTypeMember("Object")).toBeFalsy();
460
+ expect(schema.elementByCoordinate("Referencer6")).toBeUndefined();
461
+ });
216
462
 
217
- expect(schema.schemaDefinition.rootType('query')).toBeUndefined();
218
- expect(schema.type('Query')).toBeUndefined();
463
+ it(`fails to remove @inaccessible object types for breaking removals`, () => {
464
+ const schema = buildSchema(`
465
+ ${INACCESSIBLE_V02_HEADER}
466
+
467
+ # Query types can't be inaccessible
468
+ type Query @inaccessible {
469
+ someField: String
470
+ }
471
+
472
+ # Inaccessible object type
473
+ type Object @inaccessible {
474
+ someField: String
475
+ }
219
476
 
220
- expect(() => schema.validate()).toThrow();
477
+ # Inaccessible object type can't be referenced by object field in the API
478
+ # schema
479
+ type Referencer1 implements Referencer2 {
480
+ someField: Object!
481
+ }
482
+
483
+ # Inaccessible object type can't be referenced by interface field in the
484
+ # API schema
485
+ interface Referencer2 {
486
+ someField: Object
487
+ }
488
+
489
+ # Inaccessible object type can't be referenced by union member with a
490
+ # non-inaccessible parent and no non-inaccessible siblings
491
+ union Referencer3 = Object
492
+ `);
493
+
494
+ const errorMessages = expectErrors(4, () => {
495
+ removeInaccessibleElements(schema);
496
+ });
497
+
498
+ expect(errorMessages).toMatchInlineSnapshot(`
499
+ Array [
500
+ "Type \\"Object\\" is @inaccessible but is referenced by \\"Referencer1.someField\\", which is in the API schema.",
501
+ "Type \\"Object\\" is @inaccessible but is referenced by \\"Referencer2.someField\\", which is in the API schema.",
502
+ "Type \\"Query\\" is @inaccessible but is the root query type, which must be in the API schema.",
503
+ "Type \\"Referencer3\\" is in the API schema but all of its members are @inaccessible.",
504
+ ]
505
+ `);
221
506
  });
222
507
 
223
- it(`removes @inaccessible mutation root type`, () => {
508
+ it(`removes @inaccessible interface types`, () => {
224
509
  const schema = buildSchema(`
225
- directive @core(feature: String!, as: String, for: core__Purpose) repeatable on SCHEMA
510
+ ${INACCESSIBLE_V02_HEADER}
226
511
 
227
- directive @inaccessible on FIELD_DEFINITION | OBJECT | INTERFACE | UNION
512
+ type Query {
513
+ someField: String
514
+ }
228
515
 
229
- schema
230
- @core(feature: "https://specs.apollo.dev/core/v0.2")
231
- @core(feature: "https://specs.apollo.dev/inaccessible/v0.1")
232
- {
233
- query: Query
234
- mutation: Mutation
516
+ # Non-inaccessible interface type
517
+ interface VisibleInterface {
518
+ someField: String
235
519
  }
236
520
 
237
- enum core__Purpose {
238
- EXECUTION
239
- SECURITY
521
+ # Inaccessible interface type
522
+ interface Interface @inaccessible {
523
+ someField: String
240
524
  }
241
525
 
242
- type Query {
243
- fooField: Foo
526
+ # Inaccessible interface type referenced by inaccessible object field
527
+ type Referencer1 implements Referencer3 {
528
+ someField: String
529
+ privatefield: Interface! @inaccessible
244
530
  }
245
531
 
246
- type Mutation @inaccessible {
247
- fooField: Foo
532
+ # Inaccessible interface type referenced by non-inaccessible object field
533
+ # with inaccessible parent
534
+ type Referencer2 implements Referencer4 @inaccessible {
535
+ privateField: [Interface!]!
536
+ }
537
+
538
+ # Inaccessible interface type referenced by inaccessible interface field
539
+ interface Referencer3 {
540
+ someField: String
541
+ privatefield: Interface @inaccessible
542
+ }
543
+
544
+ # Inaccessible interface type referenced by non-inaccessible interface
545
+ # field with inaccessible parent
546
+ interface Referencer4 @inaccessible {
547
+ privateField: [Interface]
248
548
  }
249
549
 
250
- type Foo {
550
+ # Inaccessible interface type referenced by object type implements
551
+ type Referencer5 implements VisibleInterface & Interface {
552
+ someField: String
553
+ }
554
+
555
+ # Inaccessible interface type referenced by interface type implements
556
+ interface Referencer6 implements VisibleInterface & Interface {
251
557
  someField: String
252
558
  }
253
559
  `);
254
560
 
255
561
  removeInaccessibleElements(schema);
256
-
257
- expect(schema.schemaDefinition.rootType('mutation')).toBeUndefined();
258
- expect(schema.type('Mutation')).toBeUndefined();
562
+ schema.validate();
563
+ expect(schema.elementByCoordinate("VisibleInterface")).toBeDefined();
564
+ expect(schema.elementByCoordinate("Interface")).toBeUndefined();
565
+ expect(schema.elementByCoordinate("Referencer1.someField")).toBeDefined();
566
+ expect(
567
+ schema.elementByCoordinate("Referencer1.privatefield")
568
+ ).toBeUndefined();
569
+ expect(schema.elementByCoordinate("Referencer2")).toBeUndefined();
570
+ expect(schema.elementByCoordinate("Referencer3.someField")).toBeDefined();
571
+ expect(
572
+ schema.elementByCoordinate("Referencer3.privatefield")
573
+ ).toBeUndefined();
574
+ expect(schema.elementByCoordinate("Referencer4")).toBeUndefined();
575
+ const objectType = schema.elementByCoordinate("Referencer5");
576
+ expect(objectType instanceof ObjectType).toBeTruthy();
577
+ expect(
578
+ (objectType as ObjectType).implementsInterface("VisibleInterface")
579
+ ).toBeTruthy();
580
+ expect(
581
+ (objectType as ObjectType).implementsInterface("Interface")
582
+ ).toBeFalsy();
583
+ const interfaceType = schema.elementByCoordinate("Referencer6");
584
+ expect(interfaceType instanceof InterfaceType).toBeTruthy();
585
+ expect(
586
+ (interfaceType as InterfaceType).implementsInterface("VisibleInterface")
587
+ ).toBeTruthy();
588
+ expect(
589
+ (interfaceType as InterfaceType).implementsInterface("Interface")
590
+ ).toBeFalsy();
259
591
  });
260
592
 
261
- it(`removes @inaccessible subscription root type`, () => {
593
+ it(`fails to remove @inaccessible interface types for breaking removals`, () => {
262
594
  const schema = buildSchema(`
263
- directive @core(feature: String!, as: String, for: core__Purpose) repeatable on SCHEMA
595
+ ${INACCESSIBLE_V02_HEADER}
596
+
597
+ type Query {
598
+ someField: String
599
+ }
264
600
 
265
- directive @inaccessible on FIELD_DEFINITION | OBJECT | INTERFACE | UNION
601
+ # Inaccessible interface type
602
+ interface Interface @inaccessible {
603
+ someField: String
604
+ }
266
605
 
267
- schema
268
- @core(feature: "https://specs.apollo.dev/core/v0.2")
269
- @core(feature: "https://specs.apollo.dev/inaccessible/v0.1")
270
- {
271
- query: Query
272
- subscription: Subscription
606
+ # Inaccessible interface type can't be referenced by object field in the
607
+ # API schema
608
+ type Referencer1 implements Referencer2 {
609
+ someField: [Interface!]!
273
610
  }
274
611
 
275
- enum core__Purpose {
276
- EXECUTION
277
- SECURITY
612
+ # Inaccessible interface type can't be referenced by interface field in
613
+ # the API schema
614
+ interface Referencer2 {
615
+ someField: [Interface]
278
616
  }
617
+ `);
618
+
619
+ const errorMessages = expectErrors(2, () => {
620
+ removeInaccessibleElements(schema);
621
+ });
622
+
623
+ expect(errorMessages).toMatchInlineSnapshot(`
624
+ Array [
625
+ "Type \\"Interface\\" is @inaccessible but is referenced by \\"Referencer1.someField\\", which is in the API schema.",
626
+ "Type \\"Interface\\" is @inaccessible but is referenced by \\"Referencer2.someField\\", which is in the API schema.",
627
+ ]
628
+ `);
629
+ });
630
+
631
+ it(`removes @inaccessible union types`, () => {
632
+ const schema = buildSchema(`
633
+ ${INACCESSIBLE_V02_HEADER}
279
634
 
280
635
  type Query {
281
- fooField: Foo
636
+ someField: String
282
637
  }
283
638
 
284
- type Subscription @inaccessible {
285
- fooField: Foo
639
+ # Non-inaccessible union type
640
+ union VisibleUnion = Query
641
+
642
+ # Inaccessible union type
643
+ union Union @inaccessible = Query
644
+
645
+ # Inaccessible union type referenced by inaccessible object field
646
+ type Referencer1 implements Referencer3 {
647
+ someField: String
648
+ privatefield: Union! @inaccessible
649
+ }
650
+
651
+ # Inaccessible union type referenced by non-inaccessible object field with
652
+ # inaccessible parent
653
+ type Referencer2 implements Referencer4 @inaccessible {
654
+ privateField: [Union!]!
286
655
  }
287
656
 
288
- type Foo {
657
+ # Inaccessible union type referenced by inaccessible interface field
658
+ interface Referencer3 {
289
659
  someField: String
660
+ privatefield: Union @inaccessible
661
+ }
662
+
663
+ # Inaccessible union type referenced by non-inaccessible interface field
664
+ # with inaccessible parent
665
+ interface Referencer4 @inaccessible {
666
+ privateField: [Union]
290
667
  }
291
668
  `);
292
669
 
293
670
  removeInaccessibleElements(schema);
671
+ schema.validate();
672
+ expect(schema.elementByCoordinate("VisibleUnion")).toBeDefined();
673
+ expect(schema.elementByCoordinate("Union")).toBeUndefined();
674
+ expect(schema.elementByCoordinate("Referencer1.someField")).toBeDefined();
675
+ expect(
676
+ schema.elementByCoordinate("Referencer1.privatefield")
677
+ ).toBeUndefined();
678
+ expect(schema.elementByCoordinate("Referencer2")).toBeUndefined();
679
+ expect(schema.elementByCoordinate("Referencer3.someField")).toBeDefined();
680
+ expect(
681
+ schema.elementByCoordinate("Referencer3.privatefield")
682
+ ).toBeUndefined();
683
+ expect(schema.elementByCoordinate("Referencer4")).toBeUndefined();
684
+ });
685
+
686
+ it(`fails to remove @inaccessible union types for breaking removals`, () => {
687
+ const schema = buildSchema(`
688
+ ${INACCESSIBLE_V02_HEADER}
689
+
690
+ type Query {
691
+ someField: String
692
+ }
693
+
694
+ # Inaccessible union type
695
+ union Union @inaccessible = Query
696
+
697
+ # Inaccessible union type can't be referenced by object field in the API
698
+ # schema
699
+ type Referencer1 implements Referencer2 {
700
+ someField: Union!
701
+ }
702
+
703
+ # Inaccessible union type can't be referenced by interface field in the
704
+ # API schema
705
+ interface Referencer2 {
706
+ someField: Union
707
+ }
708
+ `);
709
+
710
+ const errorMessages = expectErrors(2, () => {
711
+ removeInaccessibleElements(schema);
712
+ });
713
+
714
+ expect(errorMessages).toMatchInlineSnapshot(`
715
+ Array [
716
+ "Type \\"Union\\" is @inaccessible but is referenced by \\"Referencer1.someField\\", which is in the API schema.",
717
+ "Type \\"Union\\" is @inaccessible but is referenced by \\"Referencer2.someField\\", which is in the API schema.",
718
+ ]
719
+ `);
720
+ });
294
721
 
295
- expect(schema.schemaDefinition.rootType('subscription')).toBeUndefined();
296
- expect(schema.type('Subscription')).toBeUndefined();
722
+ it(`removes @inaccessible input object types`, () => {
723
+ const schema = buildSchema(`
724
+ ${INACCESSIBLE_V02_HEADER}
725
+
726
+ type Query {
727
+ someField: String
728
+ }
729
+
730
+ # Non-inaccessible input object type
731
+ input VisibleInputObject {
732
+ someField: String
733
+ }
734
+
735
+ # Inaccessible input object type
736
+ input InputObject @inaccessible {
737
+ someField: String
738
+ }
739
+
740
+ # Inaccessible input object type referenced by inaccessible object field
741
+ # argument
742
+ type Referencer1 implements Referencer4 {
743
+ someField(privateArg: InputObject @inaccessible): String
744
+ }
745
+
746
+ # Inaccessible input object type referenced by non-inaccessible object
747
+ # field argument with inaccessible parent
748
+ type Referencer2 implements Referencer5 {
749
+ someField: String
750
+ privateField(privateArg: InputObject!): String @inaccessible
751
+ }
752
+
753
+ # Inaccessible input object type referenced by non-inaccessible object
754
+ # field argument with inaccessible grandparent
755
+ type Referencer3 implements Referencer6 @inaccessible {
756
+ privateField(privateArg: InputObject!): String
757
+ }
758
+
759
+ # Inaccessible input object type referenced by inaccessible interface
760
+ # field argument
761
+ interface Referencer4 {
762
+ someField(privateArg: InputObject @inaccessible): String
763
+ }
764
+
765
+ # Inaccessible input object type referenced by non-inaccessible interface
766
+ # field argument with inaccessible parent
767
+ interface Referencer5 {
768
+ someField: String
769
+ privateField(privateArg: InputObject!): String @inaccessible
770
+ }
771
+
772
+ # Inaccessible input object type referenced by non-inaccessible interface
773
+ # field argument with inaccessible grandparent
774
+ interface Referencer6 @inaccessible {
775
+ privateField(privateArg: InputObject!): String
776
+ }
777
+
778
+ # Inaccessible input object type referenced by inaccessible input object
779
+ # field
780
+ input Referencer7 {
781
+ someField: String
782
+ privateField: InputObject @inaccessible
783
+ }
784
+
785
+ # Inaccessible input object type referenced by non-inaccessible input
786
+ # object field with inaccessible parent
787
+ input Referencer8 @inaccessible {
788
+ privateField: InputObject!
789
+ }
790
+
791
+ # Inaccessible input object type referenced by inaccessible directive
792
+ # argument
793
+ directive @referencer9(privateArg: InputObject @inaccessible) on FIELD
794
+ `);
795
+
796
+ removeInaccessibleElements(schema);
797
+ schema.validate();
798
+ expect(schema.elementByCoordinate("VisibleInputObject")).toBeDefined();
799
+ expect(schema.elementByCoordinate("InputObject")).toBeUndefined();
800
+ expect(schema.elementByCoordinate("Referencer1.someField")).toBeDefined();
801
+ expect(
802
+ schema.elementByCoordinate("Referencer1.someField(privateArg:)")
803
+ ).toBeUndefined();
804
+ expect(schema.elementByCoordinate("Referencer2.someField")).toBeDefined();
805
+ expect(
806
+ schema.elementByCoordinate("Referencer2.privateField")
807
+ ).toBeUndefined();
808
+ expect(schema.elementByCoordinate("Referencer3")).toBeUndefined();
809
+ expect(schema.elementByCoordinate("Referencer4.someField")).toBeDefined();
810
+ expect(
811
+ schema.elementByCoordinate("Referencer4.someField(privateArg:)")
812
+ ).toBeUndefined();
813
+ expect(schema.elementByCoordinate("Referencer5.someField")).toBeDefined();
814
+ expect(
815
+ schema.elementByCoordinate("Referencer5.privateField")
816
+ ).toBeUndefined();
817
+ expect(schema.elementByCoordinate("Referencer6")).toBeUndefined();
818
+ expect(schema.elementByCoordinate("Referencer7.someField")).toBeDefined();
819
+ expect(
820
+ schema.elementByCoordinate("Referencer7.privatefield")
821
+ ).toBeUndefined();
822
+ expect(schema.elementByCoordinate("Referencer8")).toBeUndefined();
823
+ expect(schema.elementByCoordinate("@referencer9")).toBeDefined();
824
+ expect(
825
+ schema.elementByCoordinate("@referencer9(privateArg:)")
826
+ ).toBeUndefined();
827
+ });
828
+
829
+ it(`fails to remove @inaccessible input object types for breaking removals`, () => {
830
+ const schema = buildSchema(`
831
+ ${INACCESSIBLE_V02_HEADER}
832
+
833
+ type Query {
834
+ someField: String
835
+ }
836
+
837
+ # Inaccessible input object type
838
+ input InputObject @inaccessible {
839
+ someField: String
840
+ }
841
+
842
+ # Inaccessible input object type can't be referenced by object field
843
+ # argument in the API schema
844
+ type Referencer1 implements Referencer2 {
845
+ someField(someArg: InputObject): String
846
+ }
847
+
848
+ # Inaccessible input object type can't be referenced by interface field
849
+ # argument in the API schema
850
+ interface Referencer2 {
851
+ someField(someArg: InputObject): String
852
+ }
853
+
854
+ # Inaccessible input object type can't be referenced by input object field
855
+ # in the API schema
856
+ input Referencer3 {
857
+ someField: InputObject
858
+ }
859
+
860
+ # Inaccessible input object type can't be referenced by directive argument
861
+ # in the API schema
862
+ directive @referencer4(someArg: InputObject) on QUERY
863
+ `);
864
+
865
+ const errorMessages = expectErrors(4, () => {
866
+ removeInaccessibleElements(schema);
867
+ });
868
+
869
+ expect(errorMessages).toMatchInlineSnapshot(`
870
+ Array [
871
+ "Type \\"InputObject\\" is @inaccessible but is referenced by \\"@referencer4(someArg:)\\", which is in the API schema.",
872
+ "Type \\"InputObject\\" is @inaccessible but is referenced by \\"Referencer1.someField(someArg:)\\", which is in the API schema.",
873
+ "Type \\"InputObject\\" is @inaccessible but is referenced by \\"Referencer2.someField(someArg:)\\", which is in the API schema.",
874
+ "Type \\"InputObject\\" is @inaccessible but is referenced by \\"Referencer3.someField\\", which is in the API schema.",
875
+ ]
876
+ `);
877
+ });
878
+
879
+ it(`removes @inaccessible enum types`, () => {
880
+ const schema = buildSchema(`
881
+ ${INACCESSIBLE_V02_HEADER}
882
+
883
+ type Query {
884
+ someField: String
885
+ }
886
+
887
+ # Non-inaccessible enum type
888
+ enum VisibleEnum {
889
+ SOME_VALUE
890
+ }
891
+
892
+ # Inaccessible enum type
893
+ enum Enum @inaccessible {
894
+ SOME_VALUE
895
+ }
896
+
897
+ # Inaccessible enum type referenced by inaccessible object field
898
+ type Referencer1 implements Referencer3 {
899
+ someField: String
900
+ privatefield: Enum! @inaccessible
901
+ }
902
+
903
+ # Inaccessible enum type referenced by non-inaccessible object field with
904
+ # inaccessible parent
905
+ type Referencer2 implements Referencer4 @inaccessible {
906
+ privateField: [Enum!]!
907
+ }
908
+
909
+ # Inaccessible enum type referenced by inaccessible interface field
910
+ interface Referencer3 {
911
+ someField: String
912
+ privatefield: Enum @inaccessible
913
+ }
914
+
915
+ # Inaccessible enum type referenced by non-inaccessible interface field
916
+ # with inaccessible parent
917
+ interface Referencer4 @inaccessible {
918
+ privateField: [Enum]
919
+ }
920
+
921
+ # Inaccessible enum type referenced by inaccessible object field argument
922
+ type Referencer5 implements Referencer8 {
923
+ someField(privateArg: Enum @inaccessible): String
924
+ }
925
+
926
+ # Inaccessible enum type referenced by non-inaccessible object field
927
+ # argument with inaccessible parent
928
+ type Referencer6 implements Referencer9 {
929
+ someField: String
930
+ privateField(privateArg: Enum!): String @inaccessible
931
+ }
932
+
933
+ # Inaccessible enum type referenced by non-inaccessible object field
934
+ # argument with inaccessible grandparent
935
+ type Referencer7 implements Referencer10 @inaccessible {
936
+ privateField(privateArg: Enum!): String
937
+ }
938
+
939
+ # Inaccessible enum type referenced by inaccessible interface field
940
+ # argument
941
+ interface Referencer8 {
942
+ someField(privateArg: Enum @inaccessible): String
943
+ }
944
+
945
+ # Inaccessible enum type referenced by non-inaccessible interface field
946
+ # argument with inaccessible parent
947
+ interface Referencer9 {
948
+ someField: String
949
+ privateField(privateArg: Enum!): String @inaccessible
950
+ }
951
+
952
+ # Inaccessible enum type referenced by non-inaccessible interface field
953
+ # argument with inaccessible grandparent
954
+ interface Referencer10 @inaccessible {
955
+ privateField(privateArg: Enum!): String
956
+ }
957
+
958
+ # Inaccessible enum type referenced by inaccessible input object field
959
+ input Referencer11 {
960
+ someField: String
961
+ privateField: Enum @inaccessible
962
+ }
963
+
964
+ # Inaccessible enum type referenced by non-inaccessible input object field
965
+ # with inaccessible parent
966
+ input Referencer12 @inaccessible {
967
+ privateField: Enum!
968
+ }
969
+
970
+ # Inaccessible enum type referenced by inaccessible directive argument
971
+ directive @referencer13(privateArg: Enum @inaccessible) on FRAGMENT_DEFINITION
972
+ `);
973
+
974
+ removeInaccessibleElements(schema);
975
+ schema.validate();
976
+ expect(schema.elementByCoordinate("VisibleEnum")).toBeDefined();
977
+ expect(schema.elementByCoordinate("Enum")).toBeUndefined();
978
+ expect(schema.elementByCoordinate("Referencer1.someField")).toBeDefined();
979
+ expect(
980
+ schema.elementByCoordinate("Referencer1.privatefield")
981
+ ).toBeUndefined();
982
+ expect(schema.elementByCoordinate("Referencer2")).toBeUndefined();
983
+ expect(schema.elementByCoordinate("Referencer3.someField")).toBeDefined();
984
+ expect(
985
+ schema.elementByCoordinate("Referencer3.privatefield")
986
+ ).toBeUndefined();
987
+ expect(schema.elementByCoordinate("Referencer4")).toBeUndefined();
988
+ expect(schema.elementByCoordinate("Referencer5.someField")).toBeDefined();
989
+ expect(
990
+ schema.elementByCoordinate("Referencer5.someField(privateArg:)")
991
+ ).toBeUndefined();
992
+ expect(schema.elementByCoordinate("Referencer6.someField")).toBeDefined();
993
+ expect(
994
+ schema.elementByCoordinate("Referencer6.privateField")
995
+ ).toBeUndefined();
996
+ expect(schema.elementByCoordinate("Referencer7")).toBeUndefined();
997
+ expect(schema.elementByCoordinate("Referencer8.someField")).toBeDefined();
998
+ expect(
999
+ schema.elementByCoordinate("Referencer8.someField(privateArg:)")
1000
+ ).toBeUndefined();
1001
+ expect(schema.elementByCoordinate("Referencer9.someField")).toBeDefined();
1002
+ expect(
1003
+ schema.elementByCoordinate("Referencer9.privateField")
1004
+ ).toBeUndefined();
1005
+ expect(schema.elementByCoordinate("Referencer10")).toBeUndefined();
1006
+ expect(schema.elementByCoordinate("Referencer11.someField")).toBeDefined();
1007
+ expect(
1008
+ schema.elementByCoordinate("Referencer11.privatefield")
1009
+ ).toBeUndefined();
1010
+ expect(schema.elementByCoordinate("Referencer12")).toBeUndefined();
1011
+ expect(schema.elementByCoordinate("@referencer13")).toBeDefined();
1012
+ expect(
1013
+ schema.elementByCoordinate("@referencer13(privateArg:)")
1014
+ ).toBeUndefined();
1015
+ });
1016
+
1017
+ it(`fails to remove @inaccessible enum types for breaking removals`, () => {
1018
+ const schema = buildSchema(`
1019
+ ${INACCESSIBLE_V02_HEADER}
1020
+
1021
+ type Query {
1022
+ someField: String
1023
+ }
1024
+
1025
+ # Inaccessible enum type
1026
+ enum Enum @inaccessible {
1027
+ SOME_VALUE
1028
+ }
1029
+
1030
+ # Inaccessible enum type can't be referenced by object field in the API
1031
+ # schema
1032
+ type Referencer1 implements Referencer2 {
1033
+ somefield: [Enum!]!
1034
+ }
1035
+
1036
+ # Inaccessible enum type can't be referenced by interface field in the API
1037
+ # schema
1038
+ interface Referencer2 {
1039
+ somefield: [Enum]
1040
+ }
1041
+
1042
+ # Inaccessible enum type can't be referenced by object field argument in
1043
+ # the API schema
1044
+ type Referencer3 implements Referencer4 {
1045
+ someField(someArg: Enum): String
1046
+ }
1047
+
1048
+ # Inaccessible enum type can't be referenced by interface field argument
1049
+ # in the API schema
1050
+ interface Referencer4 {
1051
+ someField(someArg: Enum): String
1052
+ }
1053
+
1054
+ # Inaccessible enum type can't be referenced by input object field in the
1055
+ # API schema
1056
+ input Referencer5 {
1057
+ someField: Enum
1058
+ }
1059
+
1060
+ # Inaccessible enum type can't be referenced by directive argument in the
1061
+ # API schema
1062
+ directive @referencer6(someArg: Enum) on FRAGMENT_SPREAD
1063
+ `);
1064
+
1065
+ const errorMessages = expectErrors(6, () => {
1066
+ removeInaccessibleElements(schema);
1067
+ });
1068
+
1069
+ expect(errorMessages).toMatchInlineSnapshot(`
1070
+ Array [
1071
+ "Type \\"Enum\\" is @inaccessible but is referenced by \\"@referencer6(someArg:)\\", which is in the API schema.",
1072
+ "Type \\"Enum\\" is @inaccessible but is referenced by \\"Referencer1.somefield\\", which is in the API schema.",
1073
+ "Type \\"Enum\\" is @inaccessible but is referenced by \\"Referencer2.somefield\\", which is in the API schema.",
1074
+ "Type \\"Enum\\" is @inaccessible but is referenced by \\"Referencer3.someField(someArg:)\\", which is in the API schema.",
1075
+ "Type \\"Enum\\" is @inaccessible but is referenced by \\"Referencer4.someField(someArg:)\\", which is in the API schema.",
1076
+ "Type \\"Enum\\" is @inaccessible but is referenced by \\"Referencer5.someField\\", which is in the API schema.",
1077
+ ]
1078
+ `);
1079
+ });
1080
+
1081
+ it(`removes @inaccessible scalar types`, () => {
1082
+ const schema = buildSchema(`
1083
+ ${INACCESSIBLE_V02_HEADER}
1084
+
1085
+ type Query {
1086
+ someField: String
1087
+ }
1088
+
1089
+ # Non-inaccessible scalar type
1090
+ scalar VisibleScalar
1091
+
1092
+ # Inaccessible scalar type
1093
+ scalar Scalar @inaccessible
1094
+
1095
+ # Inaccessible scalar type referenced by inaccessible object field
1096
+ type Referencer1 implements Referencer3 {
1097
+ someField: String
1098
+ privatefield: Scalar! @inaccessible
1099
+ }
1100
+
1101
+ # Inaccessible scalar type referenced by non-inaccessible object field
1102
+ # with inaccessible parent
1103
+ type Referencer2 implements Referencer4 @inaccessible {
1104
+ privateField: [Scalar!]!
1105
+ }
1106
+
1107
+ # Inaccessible scalar type referenced by inaccessible interface field
1108
+ interface Referencer3 {
1109
+ someField: String
1110
+ privatefield: Scalar @inaccessible
1111
+ }
1112
+
1113
+ # Inaccessible scalar type referenced by non-inaccessible interface field
1114
+ # with inaccessible parent
1115
+ interface Referencer4 @inaccessible {
1116
+ privateField: [Scalar]
1117
+ }
1118
+
1119
+ # Inaccessible scalar type referenced by inaccessible object field
1120
+ # argument
1121
+ type Referencer5 implements Referencer8 {
1122
+ someField(privateArg: Scalar @inaccessible): String
1123
+ }
1124
+
1125
+ # Inaccessible scalar type referenced by non-inaccessible object field
1126
+ # argument with inaccessible parent
1127
+ type Referencer6 implements Referencer9 {
1128
+ someField: String
1129
+ privateField(privateArg: Scalar!): String @inaccessible
1130
+ }
1131
+
1132
+ # Inaccessible scalar type referenced by non-inaccessible object field
1133
+ # argument with inaccessible grandparent
1134
+ type Referencer7 implements Referencer10 @inaccessible {
1135
+ privateField(privateArg: Scalar!): String
1136
+ }
1137
+
1138
+ # Inaccessible scalar type referenced by inaccessible interface field
1139
+ # argument
1140
+ interface Referencer8 {
1141
+ someField(privateArg: Scalar @inaccessible): String
1142
+ }
1143
+
1144
+ # Inaccessible scalar type referenced by non-inaccessible interface field
1145
+ # argument with inaccessible parent
1146
+ interface Referencer9 {
1147
+ someField: String
1148
+ privateField(privateArg: Scalar!): String @inaccessible
1149
+ }
1150
+
1151
+ # Inaccessible scalar type referenced by non-inaccessible interface field
1152
+ # argument with inaccessible grandparent
1153
+ interface Referencer10 @inaccessible {
1154
+ privateField(privateArg: Scalar!): String
1155
+ }
1156
+
1157
+ # Inaccessible scalar type referenced by inaccessible input object field
1158
+ input Referencer11 {
1159
+ someField: String
1160
+ privateField: Scalar @inaccessible
1161
+ }
1162
+
1163
+ # Inaccessible scalar type referenced by non-inaccessible input object
1164
+ # field with inaccessible parent
1165
+ input Referencer12 @inaccessible {
1166
+ privateField: Scalar!
1167
+ }
1168
+
1169
+ # Inaccessible scalar type referenced by inaccessible directive argument
1170
+ directive @referencer13(privateArg: Scalar @inaccessible) on INLINE_FRAGMENT
1171
+ `);
1172
+
1173
+ removeInaccessibleElements(schema);
1174
+ schema.validate();
1175
+ expect(schema.elementByCoordinate("VisibleScalar")).toBeDefined();
1176
+ expect(schema.elementByCoordinate("Scalar")).toBeUndefined();
1177
+ expect(schema.elementByCoordinate("Referencer1.someField")).toBeDefined();
1178
+ expect(
1179
+ schema.elementByCoordinate("Referencer1.privatefield")
1180
+ ).toBeUndefined();
1181
+ expect(schema.elementByCoordinate("Referencer2")).toBeUndefined();
1182
+ expect(schema.elementByCoordinate("Referencer3.someField")).toBeDefined();
1183
+ expect(
1184
+ schema.elementByCoordinate("Referencer3.privatefield")
1185
+ ).toBeUndefined();
1186
+ expect(schema.elementByCoordinate("Referencer4")).toBeUndefined();
1187
+ expect(schema.elementByCoordinate("Referencer5.someField")).toBeDefined();
1188
+ expect(
1189
+ schema.elementByCoordinate("Referencer5.someField(privateArg:)")
1190
+ ).toBeUndefined();
1191
+ expect(schema.elementByCoordinate("Referencer6.someField")).toBeDefined();
1192
+ expect(
1193
+ schema.elementByCoordinate("Referencer6.privateField")
1194
+ ).toBeUndefined();
1195
+ expect(schema.elementByCoordinate("Referencer7")).toBeUndefined();
1196
+ expect(schema.elementByCoordinate("Referencer8.someField")).toBeDefined();
1197
+ expect(
1198
+ schema.elementByCoordinate("Referencer8.someField(privateArg:)")
1199
+ ).toBeUndefined();
1200
+ expect(schema.elementByCoordinate("Referencer9.someField")).toBeDefined();
1201
+ expect(
1202
+ schema.elementByCoordinate("Referencer9.privateField")
1203
+ ).toBeUndefined();
1204
+ expect(schema.elementByCoordinate("Referencer10")).toBeUndefined();
1205
+ expect(schema.elementByCoordinate("Referencer11.someField")).toBeDefined();
1206
+ expect(
1207
+ schema.elementByCoordinate("Referencer11.privatefield")
1208
+ ).toBeUndefined();
1209
+ expect(schema.elementByCoordinate("Referencer12")).toBeUndefined();
1210
+ expect(schema.elementByCoordinate("@referencer13")).toBeDefined();
1211
+ expect(
1212
+ schema.elementByCoordinate("@referencer13(privateArg:)")
1213
+ ).toBeUndefined();
1214
+ });
1215
+
1216
+ it(`fails to remove @inaccessible scalar types for breaking removals`, () => {
1217
+ const schema = buildSchema(`
1218
+ ${INACCESSIBLE_V02_HEADER}
1219
+
1220
+ type Query {
1221
+ someField: String
1222
+ }
1223
+
1224
+ # Inaccessible scalar type
1225
+ scalar Scalar @inaccessible
1226
+
1227
+ # Inaccessible scalar type can't be referenced by object field in the API
1228
+ # schema
1229
+ type Referencer1 implements Referencer2 {
1230
+ somefield: [[Scalar!]!]!
1231
+ }
1232
+
1233
+ # Inaccessible scalar type can't be referenced by interface field in the
1234
+ # API schema
1235
+ interface Referencer2 {
1236
+ somefield: [[Scalar]]
1237
+ }
1238
+
1239
+ # Inaccessible scalar type can't be referenced by object field argument in
1240
+ # the API schema
1241
+ type Referencer3 implements Referencer4 {
1242
+ someField(someArg: Scalar): String
1243
+ }
1244
+
1245
+ # Inaccessible scalar type can't be referenced by interface field argument
1246
+ # in the API schema
1247
+ interface Referencer4 {
1248
+ someField(someArg: Scalar): String
1249
+ }
1250
+
1251
+ # Inaccessible scalar type can't be referenced by input object field in
1252
+ # the API schema
1253
+ input Referencer5 {
1254
+ someField: Scalar
1255
+ }
1256
+
1257
+ # Inaccessible scalar type can't be referenced by directive argument in
1258
+ # the API schema
1259
+ directive @referencer6(someArg: Scalar) on MUTATION
1260
+ `);
1261
+
1262
+ const errorMessages = expectErrors(6, () => {
1263
+ removeInaccessibleElements(schema);
1264
+ });
1265
+
1266
+ expect(errorMessages).toMatchInlineSnapshot(`
1267
+ Array [
1268
+ "Type \\"Scalar\\" is @inaccessible but is referenced by \\"@referencer6(someArg:)\\", which is in the API schema.",
1269
+ "Type \\"Scalar\\" is @inaccessible but is referenced by \\"Referencer1.somefield\\", which is in the API schema.",
1270
+ "Type \\"Scalar\\" is @inaccessible but is referenced by \\"Referencer2.somefield\\", which is in the API schema.",
1271
+ "Type \\"Scalar\\" is @inaccessible but is referenced by \\"Referencer3.someField(someArg:)\\", which is in the API schema.",
1272
+ "Type \\"Scalar\\" is @inaccessible but is referenced by \\"Referencer4.someField(someArg:)\\", which is in the API schema.",
1273
+ "Type \\"Scalar\\" is @inaccessible but is referenced by \\"Referencer5.someField\\", which is in the API schema.",
1274
+ ]
1275
+ `);
1276
+ });
1277
+
1278
+ it(`removes @inaccessible object fields`, () => {
1279
+ const schema = buildSchema(`
1280
+ ${INACCESSIBLE_V02_HEADER}
1281
+
1282
+ extend schema {
1283
+ mutation: Mutation
1284
+ subscription: Subscription
1285
+ }
1286
+
1287
+ # Inaccessible object field on query type
1288
+ type Query {
1289
+ someField: String
1290
+ privateField: String @inaccessible
1291
+ }
1292
+
1293
+ # Inaccessible object field on mutation type
1294
+ type Mutation {
1295
+ someField: String
1296
+ privateField: String @inaccessible
1297
+ }
1298
+
1299
+ # Inaccessible object field on subscription type
1300
+ type Subscription {
1301
+ someField: String
1302
+ privateField: String @inaccessible
1303
+ }
1304
+
1305
+ # Inaccessible (and non-inaccessible) object field
1306
+ type Object implements Referencer1 & Referencer2 {
1307
+ someField: String
1308
+ privateField: String @inaccessible
1309
+ }
1310
+
1311
+ # Inaccessible object field referenced by inaccessible interface field
1312
+ interface Referencer1 {
1313
+ someField: String
1314
+ privateField: String @inaccessible
1315
+ }
1316
+
1317
+ # Inaccessible object field referenced by non-inaccessible interface field
1318
+ # with inaccessible parent
1319
+ interface Referencer2 @inaccessible {
1320
+ privateField: String
1321
+ }
1322
+
1323
+ # Inaccessible object field with an inaccessible parent and no
1324
+ # non-inaccessible siblings
1325
+ type Referencer3 @inaccessible {
1326
+ privateField: String @inaccessible
1327
+ otherPrivateField: Float @inaccessible
1328
+ }
1329
+ `);
1330
+
1331
+ removeInaccessibleElements(schema);
1332
+ schema.validate();
1333
+ expect(schema.elementByCoordinate("Query.someField")).toBeDefined();
1334
+ expect(schema.elementByCoordinate("Query.privateField")).toBeUndefined();
1335
+ expect(schema.elementByCoordinate("Mutation.someField")).toBeDefined();
1336
+ expect(schema.elementByCoordinate("Mutation.privateField")).toBeUndefined();
1337
+ expect(schema.elementByCoordinate("Subscription.someField")).toBeDefined();
1338
+ expect(
1339
+ schema.elementByCoordinate("Subscription.privateField")
1340
+ ).toBeUndefined();
1341
+ const objectType = schema.elementByCoordinate("Object");
1342
+ expect(objectType instanceof ObjectType).toBeTruthy();
1343
+ expect(
1344
+ (objectType as ObjectType).implementsInterface("Referencer1")
1345
+ ).toBeTruthy();
1346
+ expect(
1347
+ (objectType as ObjectType).implementsInterface("Referencer2")
1348
+ ).toBeFalsy();
1349
+ expect(schema.elementByCoordinate("Object.someField")).toBeDefined();
1350
+ expect(schema.elementByCoordinate("Object.privateField")).toBeUndefined();
1351
+ expect(schema.elementByCoordinate("Referencer1.someField")).toBeDefined();
1352
+ expect(
1353
+ schema.elementByCoordinate("Referencer1.privatefield")
1354
+ ).toBeUndefined();
1355
+ expect(schema.elementByCoordinate("Referencer2")).toBeUndefined();
1356
+ expect(schema.elementByCoordinate("Referencer3")).toBeUndefined();
1357
+ });
1358
+
1359
+ it(`fails to remove @inaccessible object fields for breaking removals`, () => {
1360
+ const schema = buildSchema(`
1361
+ ${INACCESSIBLE_V02_HEADER}
1362
+
1363
+ extend schema {
1364
+ mutation: Mutation
1365
+ subscription: Subscription
1366
+ }
1367
+
1368
+ # Inaccessible object field can't have a non-inaccessible parent query
1369
+ # type and no non-inaccessible siblings
1370
+ type Query {
1371
+ privateField: String @inaccessible
1372
+ otherPrivateField: Float @inaccessible
1373
+ }
1374
+
1375
+ # Inaccessible object field can't have a non-inaccessible parent mutation
1376
+ # type and no non-inaccessible siblings
1377
+ type Mutation {
1378
+ privateField: String @inaccessible
1379
+ otherPrivateField: Float @inaccessible
1380
+ }
1381
+
1382
+ # Inaccessible object field can't have a non-inaccessible parent
1383
+ # subscription type and no non-inaccessible siblings
1384
+ type Subscription {
1385
+ privateField: String @inaccessible
1386
+ otherPrivateField: Float @inaccessible
1387
+ }
1388
+
1389
+ # Inaccessible object field
1390
+ type Object implements Referencer1 {
1391
+ someField: String
1392
+ privateField: String @inaccessible
1393
+ }
1394
+
1395
+ # Inaccessible object field can't be referenced by interface field in the
1396
+ # API schema
1397
+ interface Referencer1 {
1398
+ privateField: String
1399
+ }
1400
+
1401
+ # Inaccessible object field can't have a non-inaccessible parent object
1402
+ # type and no non-inaccessible siblings
1403
+ type Referencer2 {
1404
+ privateField: String @inaccessible
1405
+ otherPrivateField: Float @inaccessible
1406
+ }
1407
+ `);
1408
+
1409
+ const errorMessages = expectErrors(5, () => {
1410
+ removeInaccessibleElements(schema);
1411
+ });
1412
+
1413
+ expect(errorMessages).toMatchInlineSnapshot(`
1414
+ Array [
1415
+ "Field \\"Object.privateField\\" is @inaccessible but implements the interface field \\"Referencer1.privateField\\", which is in the API schema.",
1416
+ "Type \\"Mutation\\" is in the API schema but all of its fields are @inaccessible.",
1417
+ "Type \\"Query\\" is in the API schema but all of its fields are @inaccessible.",
1418
+ "Type \\"Referencer2\\" is in the API schema but all of its fields are @inaccessible.",
1419
+ "Type \\"Subscription\\" is in the API schema but all of its fields are @inaccessible.",
1420
+ ]
1421
+ `);
1422
+ });
1423
+
1424
+ it(`removes @inaccessible interface fields`, () => {
1425
+ const schema = buildSchema(`
1426
+ ${INACCESSIBLE_V02_HEADER}
1427
+
1428
+ type Query {
1429
+ someField: String
1430
+ }
1431
+
1432
+ # Inaccessible (and non-inaccessible) interface field
1433
+ interface Interface implements Referencer1 & Referencer2 {
1434
+ someField: String
1435
+ privateField: String @inaccessible
1436
+ }
1437
+
1438
+ # Inaccessible interface field referenced by inaccessible interface field
1439
+ interface Referencer1 {
1440
+ someField: String
1441
+ privateField: String @inaccessible
1442
+ }
1443
+
1444
+ # Inaccessible interface field referenced by non-inaccessible interface
1445
+ # field with inaccessible parent
1446
+ interface Referencer2 @inaccessible {
1447
+ privateField: String
1448
+ }
1449
+
1450
+ # Inaccessible interface field with an inaccessible parent and no
1451
+ # non-inaccessible siblings
1452
+ interface Referencer3 @inaccessible {
1453
+ privateField: String @inaccessible
1454
+ otherPrivateField: Float @inaccessible
1455
+ }
1456
+ `);
1457
+
1458
+ removeInaccessibleElements(schema);
1459
+ schema.validate();
1460
+ const interfaceType = schema.elementByCoordinate("Interface");
1461
+ expect(interfaceType instanceof InterfaceType).toBeTruthy();
1462
+ expect(
1463
+ (interfaceType as InterfaceType).implementsInterface("Referencer1")
1464
+ ).toBeTruthy();
1465
+ expect(
1466
+ (interfaceType as InterfaceType).implementsInterface("Referencer2")
1467
+ ).toBeFalsy();
1468
+ expect(schema.elementByCoordinate("Interface.someField")).toBeDefined();
1469
+ expect(
1470
+ schema.elementByCoordinate("Interface.privateField")
1471
+ ).toBeUndefined();
1472
+ expect(schema.elementByCoordinate("Referencer1.someField")).toBeDefined();
1473
+ expect(
1474
+ schema.elementByCoordinate("Referencer1.privatefield")
1475
+ ).toBeUndefined();
1476
+ expect(schema.elementByCoordinate("Referencer2")).toBeUndefined();
1477
+ expect(schema.elementByCoordinate("Referencer3")).toBeUndefined();
1478
+ });
1479
+
1480
+ it(`fails to remove @inaccessible interface fields for breaking removals`, () => {
1481
+ const schema = buildSchema(`
1482
+ ${INACCESSIBLE_V02_HEADER}
1483
+
1484
+ type Query {
1485
+ someField: String
1486
+ }
1487
+
1488
+ # Inaccessible interface field
1489
+ interface Interface implements Referencer1 {
1490
+ someField: String
1491
+ privateField: String @inaccessible
1492
+ }
1493
+
1494
+ # Inaccessible interface field can't be referenced by interface field in
1495
+ # the API schema
1496
+ interface Referencer1 {
1497
+ privateField: String
1498
+ }
1499
+
1500
+ # Inaccessible interface field can't have a non-inaccessible parent object
1501
+ # type and no non-inaccessible siblings
1502
+ interface Referencer2 {
1503
+ privateField: String @inaccessible
1504
+ otherPrivateField: Float @inaccessible
1505
+ }
1506
+ `);
1507
+
1508
+ const errorMessages = expectErrors(2, () => {
1509
+ removeInaccessibleElements(schema);
1510
+ });
1511
+
1512
+ expect(errorMessages).toMatchInlineSnapshot(`
1513
+ Array [
1514
+ "Field \\"Interface.privateField\\" is @inaccessible but implements the interface field \\"Referencer1.privateField\\", which is in the API schema.",
1515
+ "Type \\"Referencer2\\" is in the API schema but all of its fields are @inaccessible.",
1516
+ ]
1517
+ `);
1518
+ });
1519
+
1520
+ it(`removes @inaccessible object field arguments`, () => {
1521
+ const schema = buildSchema(`
1522
+ ${INACCESSIBLE_V02_HEADER}
1523
+
1524
+ # Inaccessible object field argument in query type
1525
+ type Query {
1526
+ someField(privateArg: String @inaccessible): String
1527
+ }
1528
+
1529
+ # Inaccessible object field argument in mutation type
1530
+ type Mutation {
1531
+ someField(privateArg: String @inaccessible): String
1532
+ }
1533
+
1534
+ # Inaccessible object field argument in subscription type
1535
+ type Subscription {
1536
+ someField(privateArg: String @inaccessible): String
1537
+ }
1538
+
1539
+ # Inaccessible (and non-inaccessible) object field argument
1540
+ type Object implements Referencer1 & Referencer2 & Referencer3 {
1541
+ someField(
1542
+ someArg: String,
1543
+ privateArg: String @inaccessible
1544
+ ): String
1545
+ someOtherField: Float
1546
+ }
1547
+
1548
+ # Inaccessible object field argument referenced by inaccessible interface
1549
+ # field argument
1550
+ interface Referencer1 {
1551
+ someField(
1552
+ someArg: String,
1553
+ privateArg: String @inaccessible
1554
+ ): String
1555
+ }
1556
+
1557
+ # Inaccessible object field argument referenced by non-inaccessible
1558
+ # interface field argument with inaccessible parent
1559
+ interface Referencer2 {
1560
+ someField(
1561
+ someArg: String,
1562
+ privateArg: String
1563
+ ): String @inaccessible
1564
+ someOtherField: Float
1565
+ }
1566
+
1567
+ # Inaccessible object field argument referenced by non-inaccessible
1568
+ # interface field argument with inaccessible grandparent
1569
+ interface Referencer3 @inaccessible {
1570
+ someField(
1571
+ someArg: String,
1572
+ privateArg: String
1573
+ ): String
1574
+ }
1575
+
1576
+ # Inaccessible non-nullable object field argument with default
1577
+ type ObjectDefault {
1578
+ someField(privateArg: String! = "default" @inaccessible): String
1579
+ }
1580
+ `);
1581
+
1582
+ removeInaccessibleElements(schema);
1583
+ schema.validate();
1584
+ expect(schema.elementByCoordinate("Query.someField")).toBeDefined();
1585
+ expect(
1586
+ schema.elementByCoordinate("Query.someField(privateArg:)")
1587
+ ).toBeUndefined();
1588
+ expect(schema.elementByCoordinate("Mutation.someField")).toBeDefined();
1589
+ expect(
1590
+ schema.elementByCoordinate("Mutation.someField(privateArg:)")
1591
+ ).toBeUndefined();
1592
+ expect(schema.elementByCoordinate("Subscription.someField")).toBeDefined();
1593
+ expect(
1594
+ schema.elementByCoordinate("Subscription.someField(privateArg:)")
1595
+ ).toBeUndefined();
1596
+ const objectType = schema.elementByCoordinate("Object");
1597
+ expect(objectType instanceof ObjectType).toBeTruthy();
1598
+ expect(
1599
+ (objectType as ObjectType).implementsInterface("Referencer1")
1600
+ ).toBeTruthy();
1601
+ expect(
1602
+ (objectType as ObjectType).implementsInterface("Referencer2")
1603
+ ).toBeTruthy();
1604
+ expect(
1605
+ (objectType as ObjectType).implementsInterface("Referencer3")
1606
+ ).toBeFalsy();
1607
+ expect(
1608
+ schema.elementByCoordinate("Object.someField(someArg:)")
1609
+ ).toBeDefined();
1610
+ expect(
1611
+ schema.elementByCoordinate("Object.someField(privateArg:)")
1612
+ ).toBeUndefined();
1613
+ expect(schema.elementByCoordinate("Referencer1.someField")).toBeDefined();
1614
+ expect(
1615
+ schema.elementByCoordinate("Referencer1.someField(privateArg:)")
1616
+ ).toBeUndefined();
1617
+ expect(schema.elementByCoordinate("Referencer2")).toBeDefined();
1618
+ expect(schema.elementByCoordinate("Referencer2.someField")).toBeUndefined();
1619
+ expect(schema.elementByCoordinate("Referencer3")).toBeUndefined();
1620
+ expect(schema.elementByCoordinate("ObjectDefault.someField")).toBeDefined();
1621
+ expect(
1622
+ schema.elementByCoordinate("ObjectDefault.someField(privateArg:)")
1623
+ ).toBeUndefined();
1624
+ });
1625
+
1626
+ it(`fails to remove @inaccessible object field arguments for breaking removals`, () => {
1627
+ const schema = buildSchema(`
1628
+ ${INACCESSIBLE_V02_HEADER}
1629
+
1630
+ type Query {
1631
+ someField(someArg: String): String
1632
+ }
1633
+
1634
+ # Inaccessible object field argument
1635
+ type Object implements Referencer1 {
1636
+ someField(privateArg: String @inaccessible): String
1637
+ }
1638
+
1639
+ # Inaccessible object field argument can't be referenced by interface
1640
+ # field argument in the API schema
1641
+ interface Referencer1 {
1642
+ someField(privateArg: String): String
1643
+ }
1644
+
1645
+ # Inaccessible object field argument can't be a required argument
1646
+ type ObjectRequired {
1647
+ someField(privateArg: String! @inaccessible): String
1648
+ }
1649
+ `);
1650
+
1651
+ const errorMessages = expectErrors(2, () => {
1652
+ removeInaccessibleElements(schema);
1653
+ });
1654
+
1655
+ expect(errorMessages).toMatchInlineSnapshot(`
1656
+ Array [
1657
+ "Argument \\"Object.someField(privateArg:)\\" is @inaccessible but implements the interface argument \\"Referencer1.someField(privateArg:)\\", which is in the API schema.",
1658
+ "Argument \\"ObjectRequired.someField(privateArg:)\\" is @inaccessible but is a required argument of its field.",
1659
+ ]
1660
+ `);
1661
+ });
1662
+
1663
+ it(`removes @inaccessible interface field arguments`, () => {
1664
+ const schema = buildSchema(`
1665
+ ${INACCESSIBLE_V02_HEADER}
1666
+
1667
+ type Query {
1668
+ someField: String
1669
+ }
1670
+
1671
+ # Inaccessible (and non-inaccessible) interface field argument
1672
+ interface Interface implements Referencer1 & Referencer2 & Referencer3 {
1673
+ someField(
1674
+ someArg: String,
1675
+ privateArg: String @inaccessible
1676
+ ): String
1677
+ someOtherField: Float
1678
+ }
1679
+
1680
+ # Inaccessible interface field argument referenced by inaccessible
1681
+ # interface field argument
1682
+ interface Referencer1 {
1683
+ someField(
1684
+ someArg: String,
1685
+ privateArg: String @inaccessible
1686
+ ): String
1687
+ }
1688
+
1689
+ # Inaccessible interface field argument referenced by non-inaccessible
1690
+ # interface field argument with inaccessible parent
1691
+ interface Referencer2 {
1692
+ someField(
1693
+ someArg: String,
1694
+ privateArg: String
1695
+ ): String @inaccessible
1696
+ someOtherField: Float
1697
+ }
1698
+
1699
+ # Inaccessible interface field argument referenced by non-inaccessible
1700
+ # interface field argument with inaccessible grandparent
1701
+ interface Referencer3 @inaccessible {
1702
+ someField(
1703
+ someArg: String,
1704
+ privateArg: String
1705
+ ): String
1706
+ }
1707
+
1708
+ # Inaccessible non-nullable interface field argument with default
1709
+ interface InterfaceDefault {
1710
+ someField(privateArg: String! = "default" @inaccessible): String
1711
+ }
1712
+
1713
+ # Inaccessible interface field argument referenced by non-inaccessible
1714
+ # non-required object field argument
1715
+ type Referencer4 implements InterfaceDefault {
1716
+ someField(privateArg: String! = "default"): String
1717
+ }
1718
+
1719
+ # Inaccessible interface field argument referenced by non-inaccessible
1720
+ # required object field argument with inaccessible grandparent
1721
+ type Referencer5 implements InterfaceDefault @inaccessible {
1722
+ someField(privateArg: String!): String
1723
+ }
1724
+
1725
+ # Inaccessible interface field argument referenced by non-inaccessible
1726
+ # non-required interface field argument
1727
+ interface Referencer6 implements InterfaceDefault {
1728
+ someField(privateArg: String! = "default"): String
1729
+ }
1730
+
1731
+ # Inaccessible interface field argument referenced by non-inaccessible
1732
+ # required interface field argument with inaccessible grandparent
1733
+ interface Referencer7 implements InterfaceDefault @inaccessible {
1734
+ someField(privateArg: String!): String
1735
+ }
1736
+ `);
1737
+
1738
+ removeInaccessibleElements(schema);
1739
+ schema.validate();
1740
+ const interfaceType = schema.elementByCoordinate("Interface");
1741
+ expect(interfaceType instanceof InterfaceType).toBeTruthy();
1742
+ expect(
1743
+ (interfaceType as InterfaceType).implementsInterface("Referencer1")
1744
+ ).toBeTruthy();
1745
+ expect(
1746
+ (interfaceType as InterfaceType).implementsInterface("Referencer2")
1747
+ ).toBeTruthy();
1748
+ expect(
1749
+ (interfaceType as InterfaceType).implementsInterface("Referencer3")
1750
+ ).toBeFalsy();
1751
+ expect(
1752
+ schema.elementByCoordinate("Interface.someField(someArg:)")
1753
+ ).toBeDefined();
1754
+ expect(
1755
+ schema.elementByCoordinate("Interface.someField(privateArg:)")
1756
+ ).toBeUndefined();
1757
+ expect(schema.elementByCoordinate("Referencer1.someField")).toBeDefined();
1758
+ expect(
1759
+ schema.elementByCoordinate("Referencer1.someField(privateArg:)")
1760
+ ).toBeUndefined();
1761
+ expect(schema.elementByCoordinate("Referencer2")).toBeDefined();
1762
+ expect(schema.elementByCoordinate("Referencer2.someField")).toBeUndefined();
1763
+ expect(schema.elementByCoordinate("Referencer3")).toBeUndefined();
1764
+ expect(schema.elementByCoordinate("Interface.someField")).toBeDefined();
1765
+ expect(
1766
+ schema.elementByCoordinate("Interface.someField(privateArg:)")
1767
+ ).toBeUndefined();
1768
+ const objectArg = schema.elementByCoordinate(
1769
+ "Referencer4.someField(privateArg:)"
1770
+ );
1771
+ expect(objectArg instanceof ArgumentDefinition).toBeTruthy();
1772
+ expect(
1773
+ (
1774
+ objectArg as ArgumentDefinition<FieldDefinition<ObjectType>>
1775
+ ).isRequired()
1776
+ ).toBeFalsy();
1777
+ expect(schema.elementByCoordinate("Referencer5")).toBeUndefined();
1778
+ const interfaceArg = schema.elementByCoordinate(
1779
+ "Referencer6.someField(privateArg:)"
1780
+ );
1781
+ expect(interfaceArg instanceof ArgumentDefinition).toBeTruthy();
1782
+ expect(
1783
+ (
1784
+ interfaceArg as ArgumentDefinition<FieldDefinition<InterfaceType>>
1785
+ ).isRequired()
1786
+ ).toBeFalsy();
1787
+ expect(schema.elementByCoordinate("Referencer7")).toBeUndefined();
1788
+ });
1789
+
1790
+ it(`fails to remove @inaccessible interface field arguments for breaking removals`, () => {
1791
+ const schema = buildSchema(`
1792
+ ${INACCESSIBLE_V02_HEADER}
1793
+
1794
+ type Query {
1795
+ someField(someArg: String): String
1796
+ }
1797
+
1798
+ # Inaccessible interface field argument
1799
+ interface Interface implements Referencer1 {
1800
+ someField(privateArg: String! = "default" @inaccessible): String
1801
+ }
1802
+
1803
+ # Inaccessible interface field argument can't be referenced by interface
1804
+ # field argument in the API schema
1805
+ interface Referencer1 {
1806
+ someField(privateArg: String! = "default"): String
1807
+ }
1808
+
1809
+ # Inaccessible object field argument can't be a required argument
1810
+ type InterfaceRequired {
1811
+ someField(privateArg: String! @inaccessible): String
1812
+ }
1813
+
1814
+ # Inaccessible object field argument can't be implemented by a required
1815
+ # object field argument in the API schema
1816
+ type Referencer2 implements Interface & Referencer1 {
1817
+ someField(privateArg: String!): String
1818
+ }
1819
+
1820
+ # Inaccessible object field argument can't be implemented by a required
1821
+ # interface field argument in the API schema
1822
+ interface Referencer3 implements Interface & Referencer1 {
1823
+ someField(privateArg: String!): String
1824
+ }
1825
+ `);
1826
+
1827
+ const errorMessages = expectErrors(4, () => {
1828
+ removeInaccessibleElements(schema);
1829
+ });
1830
+
1831
+ expect(errorMessages).toMatchInlineSnapshot(`
1832
+ Array [
1833
+ "Argument \\"Interface.someField(privateArg:)\\" is @inaccessible but implements the interface argument \\"Referencer1.someField(privateArg:)\\", which is in the API schema.",
1834
+ "Argument \\"Interface.someField(privateArg:)\\" is @inaccessible but is implemented by the required argument \\"Referencer2.someField(privateArg:)\\", which is in the API schema.",
1835
+ "Argument \\"Interface.someField(privateArg:)\\" is @inaccessible but is implemented by the required argument \\"Referencer3.someField(privateArg:)\\", which is in the API schema.",
1836
+ "Argument \\"InterfaceRequired.someField(privateArg:)\\" is @inaccessible but is a required argument of its field.",
1837
+ ]
1838
+ `);
1839
+ });
1840
+
1841
+ it(`removes @inaccessible input object fields`, () => {
1842
+ const schema = buildSchema(`
1843
+ ${INACCESSIBLE_V02_HEADER}
1844
+
1845
+ type Query {
1846
+ someField: String
1847
+ }
1848
+
1849
+ # Inaccessible (and non-inaccessible) input object field
1850
+ input InputObject {
1851
+ someField: String
1852
+ privateField: String @inaccessible
1853
+ }
1854
+
1855
+ # Inaccessible input object field referenced by default value of
1856
+ # inaccessible object field argument
1857
+ type Referencer1 implements Referencer4 {
1858
+ someField(
1859
+ privateArg: InputObject = { privateField: "" } @inaccessible
1860
+ ): String
1861
+ }
1862
+
1863
+ # Inaccessible input object field referenced by default value of
1864
+ # non-inaccessible object field argument with inaccessible parent
1865
+ type Referencer2 implements Referencer5 {
1866
+ someField: String
1867
+ privateField(
1868
+ privateArg: InputObject! = { privateField: "" }
1869
+ ): String @inaccessible
1870
+ }
1871
+
1872
+ # Inaccessible input object field referenced by default value of
1873
+ # non-inaccessible object field argument with inaccessible grandparent
1874
+ type Referencer3 implements Referencer6 @inaccessible {
1875
+ privateField(privateArg: InputObject! = { privateField: "" }): String
1876
+ }
1877
+
1878
+ # Inaccessible input object field referenced by default value of
1879
+ # inaccessible interface field argument
1880
+ interface Referencer4 {
1881
+ someField(
1882
+ privateArg: InputObject = { privateField: "" } @inaccessible
1883
+ ): String
1884
+ }
1885
+
1886
+ # Inaccessible input object field referenced by default value of
1887
+ # non-inaccessible interface field argument with inaccessible parent
1888
+ interface Referencer5 {
1889
+ someField: String
1890
+ privateField(
1891
+ privateArg: InputObject! = { privateField: "" }
1892
+ ): String @inaccessible
1893
+ }
1894
+
1895
+ # Inaccessible input object field referenced by default value of
1896
+ # non-inaccessible interface field argument with inaccessible grandparent
1897
+ interface Referencer6 @inaccessible {
1898
+ privateField(privateArg: InputObject! = { privateField: "" }): String
1899
+ }
1900
+
1901
+ # Inaccessible input object field referenced by default value of
1902
+ # inaccessible input object field
1903
+ input Referencer7 {
1904
+ someField: String
1905
+ privateField: InputObject = { privateField: "" } @inaccessible
1906
+ }
1907
+
1908
+ # Inaccessible input object field referenced by default value of
1909
+ # non-inaccessible input object field with inaccessible parent
1910
+ input Referencer8 @inaccessible {
1911
+ privateField: InputObject! = { privateField: "" }
1912
+ }
1913
+
1914
+ # Inaccessible input object field referenced by default value of
1915
+ # inaccessible directive argument
1916
+ directive @referencer9(
1917
+ privateArg: InputObject = { privateField: "" } @inaccessible
1918
+ ) on SUBSCRIPTION
1919
+
1920
+ # Inaccessible input object field not referenced (but type is referenced)
1921
+ # by default value of object field argument in the API schema
1922
+ type Referencer10 {
1923
+ someField(privateArg: InputObject = { someField: "" }): String
1924
+ }
1925
+
1926
+ # Inaccessible input object field with an inaccessible parent and no
1927
+ # non-inaccessible siblings
1928
+ input Referencer11 @inaccessible {
1929
+ privateField: String @inaccessible
1930
+ otherPrivateField: Float @inaccessible
1931
+ }
1932
+
1933
+ # Inaccessible non-nullable input object field with default
1934
+ input InputObjectDefault {
1935
+ someField: String
1936
+ privateField: String! = "default" @inaccessible
1937
+ }
1938
+ `);
1939
+
1940
+ removeInaccessibleElements(schema);
1941
+ schema.validate();
1942
+ expect(schema.elementByCoordinate("InputObject.someField")).toBeDefined();
1943
+ expect(
1944
+ schema.elementByCoordinate("InputObject.privateField")
1945
+ ).toBeUndefined();
1946
+ expect(schema.elementByCoordinate("Referencer1.someField")).toBeDefined();
1947
+ expect(
1948
+ schema.elementByCoordinate("Referencer1.someField(privateArg:)")
1949
+ ).toBeUndefined();
1950
+ expect(schema.elementByCoordinate("Referencer2.someField")).toBeDefined();
1951
+ expect(
1952
+ schema.elementByCoordinate("Referencer2.privateField")
1953
+ ).toBeUndefined();
1954
+ expect(schema.elementByCoordinate("Referencer3")).toBeUndefined();
1955
+ expect(schema.elementByCoordinate("Referencer4.someField")).toBeDefined();
1956
+ expect(
1957
+ schema.elementByCoordinate("Referencer4.someField(privateArg:)")
1958
+ ).toBeUndefined();
1959
+ expect(schema.elementByCoordinate("Referencer5.someField")).toBeDefined();
1960
+ expect(
1961
+ schema.elementByCoordinate("Referencer5.privateField")
1962
+ ).toBeUndefined();
1963
+ expect(schema.elementByCoordinate("Referencer6")).toBeUndefined();
1964
+ expect(schema.elementByCoordinate("Referencer7.someField")).toBeDefined();
1965
+ expect(
1966
+ schema.elementByCoordinate("Referencer7.privatefield")
1967
+ ).toBeUndefined();
1968
+ expect(schema.elementByCoordinate("Referencer8")).toBeUndefined();
1969
+ expect(schema.elementByCoordinate("@referencer9")).toBeDefined();
1970
+ expect(
1971
+ schema.elementByCoordinate("@referencer9(privateArg:)")
1972
+ ).toBeUndefined();
1973
+ expect(
1974
+ schema.elementByCoordinate("Referencer10.someField(privateArg:)")
1975
+ ).toBeDefined();
1976
+ expect(schema.elementByCoordinate("Referencer11")).toBeUndefined();
1977
+ expect(
1978
+ schema.elementByCoordinate("InputObjectDefault.someField")
1979
+ ).toBeDefined();
1980
+ expect(
1981
+ schema.elementByCoordinate("InputObjectDefault.privatefield")
1982
+ ).toBeUndefined();
1983
+ });
1984
+
1985
+ it(`fails to remove @inaccessible input object fields for breaking removals`, () => {
1986
+ const schema = buildSchema(`
1987
+ ${INACCESSIBLE_V02_HEADER}
1988
+
1989
+ type Query {
1990
+ someField: String
1991
+ }
1992
+
1993
+ # Inaccessible input object field
1994
+ input InputObject {
1995
+ someField: String
1996
+ privateField: String @inaccessible
1997
+ }
1998
+
1999
+ # Inaccessible input object field can't be referenced by default value of
2000
+ # object field argument in the API schema
2001
+ type Referencer1 implements Referencer2 {
2002
+ someField(someArg: InputObject = { privateField: "" }): String
2003
+ }
2004
+
2005
+ # Inaccessible input object field can't be referenced by default value of
2006
+ # interface field argument in the API schema
2007
+ interface Referencer2 {
2008
+ someField(someArg: InputObject = { privateField: "" }): String
2009
+ }
2010
+
2011
+ # Inaccessible input object field can't be referenced by default value of
2012
+ # input object field in the API schema
2013
+ input Referencer3 {
2014
+ someField: InputObject = { privateField: "" }
2015
+ }
2016
+
2017
+ # Inaccessible input object field can't be referenced by default value of
2018
+ # directive argument in the API schema
2019
+ directive @referencer4(
2020
+ someArg: InputObject = { privateField: "" }
2021
+ ) on FIELD
2022
+
2023
+ # Inaccessible input object field can't have a non-inaccessible parent
2024
+ # and no non-inaccessible siblings
2025
+ input Referencer5 {
2026
+ privateField: String @inaccessible
2027
+ otherPrivateField: Float @inaccessible
2028
+ }
2029
+
2030
+ # Inaccessible input object field can't be a required field
2031
+ input InputObjectRequired {
2032
+ someField: String
2033
+ privateField: String! @inaccessible
2034
+ }
2035
+ `);
2036
+
2037
+ const errorMessages = expectErrors(6, () => {
2038
+ removeInaccessibleElements(schema);
2039
+ });
2040
+
2041
+ expect(errorMessages).toMatchInlineSnapshot(`
2042
+ Array [
2043
+ "Input field \\"InputObject.privateField\\" is @inaccessible but is used in the default value of \\"@referencer4(someArg:)\\", which is in the API schema.",
2044
+ "Input field \\"InputObject.privateField\\" is @inaccessible but is used in the default value of \\"Referencer1.someField(someArg:)\\", which is in the API schema.",
2045
+ "Input field \\"InputObject.privateField\\" is @inaccessible but is used in the default value of \\"Referencer2.someField(someArg:)\\", which is in the API schema.",
2046
+ "Input field \\"InputObject.privateField\\" is @inaccessible but is used in the default value of \\"Referencer3.someField\\", which is in the API schema.",
2047
+ "Input field \\"InputObjectRequired.privateField\\" is @inaccessible but is a required input field of its type.",
2048
+ "Type \\"Referencer5\\" is in the API schema but all of its input fields are @inaccessible.",
2049
+ ]
2050
+ `);
2051
+ });
2052
+
2053
+ it(`removes @inaccessible enum values`, () => {
2054
+ const schema = buildSchema(`
2055
+ ${INACCESSIBLE_V02_HEADER}
2056
+
2057
+ type Query {
2058
+ someField: String
2059
+ }
2060
+
2061
+ # Inaccessible (and non-inaccessible) enum value
2062
+ enum Enum {
2063
+ SOME_VALUE
2064
+ PRIVATE_VALUE @inaccessible
2065
+ }
2066
+
2067
+ # Inaccessible enum value referenced by default value of inaccessible
2068
+ # object field argument
2069
+ type Referencer1 implements Referencer4 {
2070
+ someField(
2071
+ privateArg: Enum = PRIVATE_VALUE @inaccessible
2072
+ ): String
2073
+ }
2074
+
2075
+ # Inaccessible enum value referenced by default value of non-inaccessible
2076
+ # object field argument with inaccessible parent
2077
+ type Referencer2 implements Referencer5 {
2078
+ someField: String
2079
+ privateField(
2080
+ privateArg: Enum! = PRIVATE_VALUE
2081
+ ): String @inaccessible
2082
+ }
2083
+
2084
+ # Inaccessible enum value referenced by default value of non-inaccessible
2085
+ # object field argument with inaccessible grandparent
2086
+ type Referencer3 implements Referencer6 @inaccessible {
2087
+ privateField(privateArg: Enum! = PRIVATE_VALUE): String
2088
+ }
2089
+
2090
+ # Inaccessible enum value referenced by default value of inaccessible
2091
+ # interface field argument
2092
+ interface Referencer4 {
2093
+ someField(
2094
+ privateArg: Enum = PRIVATE_VALUE @inaccessible
2095
+ ): String
2096
+ }
2097
+
2098
+ # Inaccessible enum value referenced by default value of non-inaccessible
2099
+ # interface field argument with inaccessible parent
2100
+ interface Referencer5 {
2101
+ someField: String
2102
+ privateField(
2103
+ privateArg: Enum! = PRIVATE_VALUE
2104
+ ): String @inaccessible
2105
+ }
2106
+
2107
+ # Inaccessible enum value referenced by default value of non-inaccessible
2108
+ # interface field argument with inaccessible grandparent
2109
+ interface Referencer6 @inaccessible {
2110
+ privateField(privateArg: Enum! = PRIVATE_VALUE): String
2111
+ }
2112
+
2113
+ # Inaccessible enum value referenced by default value of inaccessible
2114
+ # input object field
2115
+ input Referencer7 {
2116
+ someField: String
2117
+ privateField: Enum = PRIVATE_VALUE @inaccessible
2118
+ }
2119
+
2120
+ # Inaccessible enum value referenced by default value of non-inaccessible
2121
+ # input object field with inaccessible parent
2122
+ input Referencer8 @inaccessible {
2123
+ privateField: Enum! = PRIVATE_VALUE
2124
+ }
2125
+
2126
+ # Inaccessible enum value referenced by default value of inaccessible
2127
+ # directive argument
2128
+ directive @referencer9(
2129
+ privateArg: Enum = PRIVATE_VALUE @inaccessible
2130
+ ) on FRAGMENT_SPREAD
2131
+
2132
+ # Inaccessible enum value not referenced (but type is referenced) by
2133
+ # default value of object field argument in the API schema
2134
+ type Referencer10 {
2135
+ someField(privateArg: Enum = SOME_VALUE): String
2136
+ }
2137
+
2138
+ # Inaccessible enum value with an inaccessible parent and no
2139
+ # non-inaccessible siblings
2140
+ enum Referencer11 @inaccessible {
2141
+ PRIVATE_VALUE @inaccessible
2142
+ OTHER_PRIVATE_VALUE @inaccessible
2143
+ }
2144
+ `);
2145
+
2146
+ removeInaccessibleElements(schema);
2147
+ schema.validate();
2148
+ expect(schema.elementByCoordinate("Enum.SOME_VALUE")).toBeDefined();
2149
+ expect(schema.elementByCoordinate("Enum.PRIVATE_VALUE")).toBeUndefined();
2150
+ expect(schema.elementByCoordinate("Referencer1.someField")).toBeDefined();
2151
+ expect(
2152
+ schema.elementByCoordinate("Referencer1.someField(privateArg:)")
2153
+ ).toBeUndefined();
2154
+ expect(schema.elementByCoordinate("Referencer2.someField")).toBeDefined();
2155
+ expect(
2156
+ schema.elementByCoordinate("Referencer2.privateField")
2157
+ ).toBeUndefined();
2158
+ expect(schema.elementByCoordinate("Referencer3")).toBeUndefined();
2159
+ expect(schema.elementByCoordinate("Referencer4.someField")).toBeDefined();
2160
+ expect(
2161
+ schema.elementByCoordinate("Referencer4.someField(privateArg:)")
2162
+ ).toBeUndefined();
2163
+ expect(schema.elementByCoordinate("Referencer5.someField")).toBeDefined();
2164
+ expect(
2165
+ schema.elementByCoordinate("Referencer5.privateField")
2166
+ ).toBeUndefined();
2167
+ expect(schema.elementByCoordinate("Referencer6")).toBeUndefined();
2168
+ expect(schema.elementByCoordinate("Referencer7.someField")).toBeDefined();
2169
+ expect(
2170
+ schema.elementByCoordinate("Referencer7.privatefield")
2171
+ ).toBeUndefined();
2172
+ expect(schema.elementByCoordinate("Referencer8")).toBeUndefined();
2173
+ expect(schema.elementByCoordinate("@referencer9")).toBeDefined();
2174
+ expect(
2175
+ schema.elementByCoordinate("@referencer9(privateArg:)")
2176
+ ).toBeUndefined();
2177
+ expect(
2178
+ schema.elementByCoordinate("Referencer10.someField(privateArg:)")
2179
+ ).toBeDefined();
2180
+ expect(schema.elementByCoordinate("Referencer11")).toBeUndefined();
2181
+ });
2182
+
2183
+ it(`fails to remove @inaccessible enum values for breaking removals`, () => {
2184
+ const schema = buildSchema(`
2185
+ ${INACCESSIBLE_V02_HEADER}
2186
+
2187
+ type Query {
2188
+ someField: String
2189
+ }
2190
+
2191
+ # Inaccessible enum value
2192
+ enum Enum {
2193
+ SOME_VALUE
2194
+ PRIVATE_VALUE @inaccessible
2195
+ }
2196
+
2197
+ # Inaccessible enum value can't be referenced by default value of object
2198
+ # field argument in the API schema
2199
+ type Referencer1 implements Referencer2 {
2200
+ someField(someArg: Enum = PRIVATE_VALUE): String
2201
+ }
2202
+
2203
+ # Inaccessible enum value can't be referenced by default value of
2204
+ # interface field argument in the API schema
2205
+ interface Referencer2 {
2206
+ someField(someArg: Enum = PRIVATE_VALUE): String
2207
+ }
2208
+
2209
+ # Inaccessible enum value can't be referenced by default value of input
2210
+ # object field in the API schema
2211
+ input Referencer3 {
2212
+ someField: Enum = PRIVATE_VALUE
2213
+ }
2214
+
2215
+ # Inaccessible input enum value can't be referenced by default value of
2216
+ # directive argument in the API schema
2217
+ directive @referencer4(someArg: Enum = PRIVATE_VALUE) on INLINE_FRAGMENT
2218
+
2219
+ # Inaccessible enum value can't have a non-inaccessible parent and no
2220
+ # non-inaccessible siblings
2221
+ enum Referencer5 {
2222
+ PRIVATE_VALUE @inaccessible
2223
+ OTHER_PRIVATE_VALUE @inaccessible
2224
+ }
2225
+ `);
2226
+
2227
+ const errorMessages = expectErrors(5, () => {
2228
+ removeInaccessibleElements(schema);
2229
+ });
2230
+
2231
+ expect(errorMessages).toMatchInlineSnapshot(`
2232
+ Array [
2233
+ "Enum value \\"Enum.PRIVATE_VALUE\\" is @inaccessible but is used in the default value of \\"@referencer4(someArg:)\\", which is in the API schema.",
2234
+ "Enum value \\"Enum.PRIVATE_VALUE\\" is @inaccessible but is used in the default value of \\"Referencer1.someField(someArg:)\\", which is in the API schema.",
2235
+ "Enum value \\"Enum.PRIVATE_VALUE\\" is @inaccessible but is used in the default value of \\"Referencer2.someField(someArg:)\\", which is in the API schema.",
2236
+ "Enum value \\"Enum.PRIVATE_VALUE\\" is @inaccessible but is used in the default value of \\"Referencer3.someField\\", which is in the API schema.",
2237
+ "Type \\"Referencer5\\" is in the API schema but all of its values are @inaccessible.",
2238
+ ]
2239
+ `);
2240
+ });
2241
+
2242
+ it(`removes @inaccessible directive arguments`, () => {
2243
+ const schema = buildSchema(`
2244
+ ${INACCESSIBLE_V02_HEADER}
2245
+
2246
+ type Query {
2247
+ someField: String
2248
+ }
2249
+
2250
+ # Inaccessible (and non-inaccessible) directive argument
2251
+ directive @directive(
2252
+ someArg: String
2253
+ privateArg: String @inaccessible
2254
+ ) on QUERY
2255
+
2256
+ # Inaccessible non-nullable directive argument with default
2257
+ directive @directiveDefault(
2258
+ someArg: String
2259
+ privateArg: String! = "default" @inaccessible
2260
+ ) on MUTATION
2261
+ `);
2262
+
2263
+ removeInaccessibleElements(schema);
2264
+ schema.validate();
2265
+ expect(schema.elementByCoordinate("@directive(someArg:)")).toBeDefined();
2266
+ expect(
2267
+ schema.elementByCoordinate("@directive(privateArg:)")
2268
+ ).toBeUndefined();
2269
+ expect(
2270
+ schema.elementByCoordinate("@directiveDefault(someArg:)")
2271
+ ).toBeDefined();
2272
+ expect(
2273
+ schema.elementByCoordinate("@directiveDefault(privateArg:)")
2274
+ ).toBeUndefined();
2275
+ });
2276
+
2277
+ it(`fails to remove @inaccessible directive arguments for breaking removals`, () => {
2278
+ const schema = buildSchema(`
2279
+ ${INACCESSIBLE_V02_HEADER}
2280
+
2281
+ type Query {
2282
+ someField: String
2283
+ }
2284
+
2285
+ # Inaccessible directive argument
2286
+ directive @directive(privateArg: String @inaccessible) on SUBSCRIPTION
2287
+
2288
+ # Inaccessible directive argument can't be a required field
2289
+ directive @directiveRequired(
2290
+ someArg: String
2291
+ privateArg: String! @inaccessible
2292
+ ) on FRAGMENT_DEFINITION
2293
+ `);
2294
+
2295
+ const errorMessages = expectErrors(1, () => {
2296
+ removeInaccessibleElements(schema);
2297
+ });
2298
+
2299
+ expect(errorMessages).toMatchInlineSnapshot(`
2300
+ Array [
2301
+ "Argument \\"@directiveRequired(privateArg:)\\" is @inaccessible but is a required argument of its directive.",
2302
+ ]
2303
+ `);
2304
+ });
2305
+
2306
+ it(`handles complex default values`, () => {
2307
+ const schema = buildSchema(`
2308
+ ${INACCESSIBLE_V02_HEADER}
2309
+
2310
+ type Query {
2311
+ someField(arg1: [[RootInputObject!]]! = [
2312
+ {
2313
+ foo: {
2314
+ # 2 references (with nesting)
2315
+ privateField: [PRIVATE_VALUE]
2316
+ }
2317
+ bar: SOME_VALUE
2318
+ # 0 references since scalar
2319
+ baz: { privateField: PRIVATE_VALUE }
2320
+ },
2321
+ [{
2322
+ foo: [{
2323
+ someField: "foo"
2324
+ }]
2325
+ # 1 reference
2326
+ bar: PRIVATE_VALUE
2327
+ }]
2328
+ ]): String
2329
+ }
2330
+
2331
+ input RootInputObject {
2332
+ foo: [NestedInputObject]
2333
+ bar: Enum!
2334
+ baz: Scalar! = { default: 4 }
2335
+ }
2336
+
2337
+ input NestedInputObject {
2338
+ someField: String
2339
+ privateField: [Enum!] @inaccessible
2340
+ }
2341
+
2342
+ enum Enum {
2343
+ SOME_VALUE
2344
+ PRIVATE_VALUE @inaccessible
2345
+ }
2346
+
2347
+ scalar Scalar
2348
+ `);
2349
+
2350
+ const errorMessages = expectErrors(3, () => {
2351
+ removeInaccessibleElements(schema);
2352
+ });
2353
+
2354
+ expect(errorMessages).toMatchInlineSnapshot(`
2355
+ Array [
2356
+ "Enum value \\"Enum.PRIVATE_VALUE\\" is @inaccessible but is used in the default value of \\"Query.someField(arg1:)\\", which is in the API schema.",
2357
+ "Enum value \\"Enum.PRIVATE_VALUE\\" is @inaccessible but is used in the default value of \\"Query.someField(arg1:)\\", which is in the API schema.",
2358
+ "Input field \\"NestedInputObject.privateField\\" is @inaccessible but is used in the default value of \\"Query.someField(arg1:)\\", which is in the API schema.",
2359
+ ]
2360
+ `);
2361
+ });
2362
+
2363
+ // It's not GraphQL-spec-compliant to allow a string for an enum value, but
2364
+ // since we're allowing it, we need to make sure this logic keeps working
2365
+ // until we're allowed to make breaking changes and remove it.
2366
+ it(`handles string enum value in default value`, () => {
2367
+ const schema = buildSchema(`
2368
+ ${INACCESSIBLE_V02_HEADER}
2369
+
2370
+ type Query {
2371
+ someField(arg1: Enum! = "PRIVATE_VALUE"): String
2372
+ }
2373
+
2374
+ enum Enum {
2375
+ SOME_VALUE
2376
+ PRIVATE_VALUE @inaccessible
2377
+ }
2378
+ `);
2379
+
2380
+ const errorMessages = expectErrors(1, () => {
2381
+ removeInaccessibleElements(schema);
2382
+ });
2383
+
2384
+ expect(errorMessages).toMatchInlineSnapshot(`
2385
+ Array [
2386
+ "Enum value \\"Enum.PRIVATE_VALUE\\" is @inaccessible but is used in the default value of \\"Query.someField(arg1:)\\", which is in the API schema.",
2387
+ ]
2388
+ `);
297
2389
  });
298
2390
  });