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