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

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