@apollo/federation-internals 2.4.5 → 2.4.6
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.
- package/dist/operations.d.ts +6 -3
- package/dist/operations.d.ts.map +1 -1
- package/dist/operations.js +39 -11
- package/dist/operations.js.map +1 -1
- package/dist/utils.d.ts +1 -1
- package/dist/utils.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/operations.ts +145 -20
- package/src/utils.ts +1 -1
- package/CHANGELOG.md +0 -211
- package/jest.config.js +0 -11
- package/src/__tests__/coreSpec.test.ts +0 -212
- package/src/__tests__/definitions.test.ts +0 -982
- package/src/__tests__/directiveAndTypeSpecifications.test.ts +0 -41
- package/src/__tests__/extractSubgraphsFromSupergraph.test.ts +0 -748
- package/src/__tests__/federation.test.ts +0 -31
- package/src/__tests__/graphQLJSSchemaToAST.test.ts +0 -156
- package/src/__tests__/matchers/index.ts +0 -1
- package/src/__tests__/matchers/toMatchString.ts +0 -87
- package/src/__tests__/operations.test.ts +0 -1266
- package/src/__tests__/removeInaccessibleElements.test.ts +0 -2471
- package/src/__tests__/schemaUpgrader.test.ts +0 -287
- package/src/__tests__/subgraphValidation.test.ts +0 -1254
- package/src/__tests__/supergraphSdl.graphql +0 -281
- package/src/__tests__/testUtils.ts +0 -28
- package/src/__tests__/toAPISchema.test.ts +0 -53
- package/src/__tests__/tsconfig.json +0 -7
- package/src/__tests__/utils.test.ts +0 -92
- package/src/__tests__/values.test.ts +0 -390
- package/tsconfig.json +0 -10
- package/tsconfig.test.json +0 -8
- package/tsconfig.tsbuildinfo +0 -1
|
@@ -1,287 +0,0 @@
|
|
|
1
|
-
import { FEDERATION2_LINK_WITH_FULL_IMPORTS } from '..';
|
|
2
|
-
import { ObjectType } from '../definitions';
|
|
3
|
-
import { buildSubgraph, Subgraphs } from '../federation';
|
|
4
|
-
import { UpgradeChangeID, UpgradeResult, upgradeSubgraphsIfNecessary } from '../schemaUpgrader';
|
|
5
|
-
import './matchers';
|
|
6
|
-
|
|
7
|
-
function changeMessages(res: UpgradeResult, subgraphName: string, id: UpgradeChangeID): string[] {
|
|
8
|
-
const changes = res.changes?.get(subgraphName)?.get(id);
|
|
9
|
-
return changes?.map(c => c.toString()) ?? [];
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* A lot of the schema upgrader behaviors are tested as part of composing fed1 schema in `composeFed1Subgraphs.test.ts`.
|
|
14
|
-
* This test file thus mostly focuses on the change-reporting of the schema upgrader.
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
test('upgrade complex schema', () => {
|
|
18
|
-
const s1 = `
|
|
19
|
-
type Query {
|
|
20
|
-
products: [Product!]! @provides(fields: "upc description")
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
interface I @key(fields: "upc") {
|
|
24
|
-
upc: ID!
|
|
25
|
-
description: String @external
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
extend type Product implements I @key(fields: "upc") {
|
|
29
|
-
upc: ID! @external
|
|
30
|
-
name: String @external
|
|
31
|
-
inventory: Int @requires(fields: "upc")
|
|
32
|
-
description: String @external
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
# A type with a genuine 'graphqQL' extension, to ensure the extend don't get removed.
|
|
36
|
-
type Random {
|
|
37
|
-
x: Int @provides(fields: "x")
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
extend type Random {
|
|
41
|
-
y: Int
|
|
42
|
-
}
|
|
43
|
-
`;
|
|
44
|
-
|
|
45
|
-
// Note that no changes are really expected on that 2nd schema: it is just there to make the example not throw due to
|
|
46
|
-
// then Product type extension having no "base".
|
|
47
|
-
const s2 = `
|
|
48
|
-
type Product @key(fields: "upc") {
|
|
49
|
-
upc: ID!
|
|
50
|
-
name: String
|
|
51
|
-
description: String
|
|
52
|
-
}
|
|
53
|
-
`;
|
|
54
|
-
|
|
55
|
-
const subgraphs = new Subgraphs();
|
|
56
|
-
subgraphs.add(buildSubgraph('s1', 'http://s1', s1));
|
|
57
|
-
subgraphs.add(buildSubgraph('s2', 'http://s1', s2));
|
|
58
|
-
const res = upgradeSubgraphsIfNecessary(subgraphs);
|
|
59
|
-
expect(res.errors).toBeUndefined();
|
|
60
|
-
|
|
61
|
-
expect(changeMessages(res, 's1', 'EXTERNAL_ON_TYPE_EXTENSION_REMOVAL')).toStrictEqual([
|
|
62
|
-
'Removed @external from field "Product.upc" as it is a key of an extension type'
|
|
63
|
-
]);
|
|
64
|
-
|
|
65
|
-
expect(changeMessages(res, 's1', 'TYPE_EXTENSION_REMOVAL')).toStrictEqual([
|
|
66
|
-
'Switched type "Product" from an extension to a definition'
|
|
67
|
-
]);
|
|
68
|
-
|
|
69
|
-
expect(changeMessages(res, 's1', 'UNUSED_EXTERNAL_REMOVAL')).toStrictEqual([
|
|
70
|
-
'Removed @external field "Product.name" as it was not used in any @key, @provides or @requires'
|
|
71
|
-
]);
|
|
72
|
-
|
|
73
|
-
expect(changeMessages(res, 's1', 'EXTERNAL_ON_INTERFACE_REMOVAL')).toStrictEqual([
|
|
74
|
-
'Removed @external directive on interface type field "I.description": @external is nonsensical on interface fields'
|
|
75
|
-
]);
|
|
76
|
-
|
|
77
|
-
expect(changeMessages(res, 's1', 'INACTIVE_PROVIDES_OR_REQUIRES_REMOVAL')).toStrictEqual([
|
|
78
|
-
'Removed directive @requires(fields: "upc") on "Product.inventory": none of the fields were truly @external'
|
|
79
|
-
]);
|
|
80
|
-
|
|
81
|
-
expect(changeMessages(res, 's1', 'INACTIVE_PROVIDES_OR_REQUIRES_FIELDS_REMOVAL')).toStrictEqual([
|
|
82
|
-
'Updated directive @provides(fields: "upc description") on "Query.products" to @provides(fields: "description"): removed fields that were not truly @external'
|
|
83
|
-
]);
|
|
84
|
-
|
|
85
|
-
expect(changeMessages(res, 's1', 'KEY_ON_INTERFACE_REMOVAL')).toStrictEqual([
|
|
86
|
-
'Removed @key on interface "I": while allowed by federation 0.x, @key on interfaces were completely ignored/had no effect'
|
|
87
|
-
]);
|
|
88
|
-
|
|
89
|
-
expect(changeMessages(res, 's1', 'PROVIDES_ON_NON_COMPOSITE_REMOVAL')).toStrictEqual([
|
|
90
|
-
'Removed @provides directive on field "Random.x" as it is of non-composite type "Int": while not rejected by federation 0.x, such @provide is nonsensical and was ignored'
|
|
91
|
-
]);
|
|
92
|
-
|
|
93
|
-
expect(res.subgraphs?.get('s1')?.toString()).toMatchString(`
|
|
94
|
-
schema
|
|
95
|
-
${FEDERATION2_LINK_WITH_FULL_IMPORTS}
|
|
96
|
-
{
|
|
97
|
-
query: Query
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
type Query {
|
|
101
|
-
products: [Product!]! @provides(fields: "description")
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
interface I {
|
|
105
|
-
upc: ID!
|
|
106
|
-
description: String
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
type Product implements I
|
|
110
|
-
@key(fields: "upc")
|
|
111
|
-
{
|
|
112
|
-
upc: ID!
|
|
113
|
-
inventory: Int
|
|
114
|
-
description: String @external
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
type Random {
|
|
118
|
-
x: Int
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
extend type Random {
|
|
122
|
-
y: Int
|
|
123
|
-
}
|
|
124
|
-
`);
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
test('update federation directive non-string arguments', () => {
|
|
128
|
-
const s = `
|
|
129
|
-
type Query {
|
|
130
|
-
a: A
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
type A @key(fields: id) @key(fields: ["id", "x"]) {
|
|
134
|
-
id: String
|
|
135
|
-
x: Int
|
|
136
|
-
}
|
|
137
|
-
`;
|
|
138
|
-
|
|
139
|
-
const subgraphs = new Subgraphs();
|
|
140
|
-
subgraphs.add(buildSubgraph('s', 'http://s', s));
|
|
141
|
-
const res = upgradeSubgraphsIfNecessary(subgraphs);
|
|
142
|
-
expect(res.errors).toBeUndefined();
|
|
143
|
-
|
|
144
|
-
expect(changeMessages(res, 's', 'FIELDS_ARGUMENT_COERCION_TO_STRING')).toStrictEqual([
|
|
145
|
-
'Coerced "fields" argument for directive @key for "A" into a string: coerced from @key(fields: id) to @key(fields: "id")',
|
|
146
|
-
'Coerced "fields" argument for directive @key for "A" into a string: coerced from @key(fields: ["id", "x"]) to @key(fields: "id x")',
|
|
147
|
-
]);
|
|
148
|
-
|
|
149
|
-
expect(res.subgraphs?.get('s')?.toString()).toMatchString(`
|
|
150
|
-
schema
|
|
151
|
-
${FEDERATION2_LINK_WITH_FULL_IMPORTS}
|
|
152
|
-
{
|
|
153
|
-
query: Query
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
type Query {
|
|
157
|
-
a: A
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
type A
|
|
161
|
-
@key(fields: "id")
|
|
162
|
-
@key(fields: "id x")
|
|
163
|
-
{
|
|
164
|
-
id: String
|
|
165
|
-
x: Int
|
|
166
|
-
}
|
|
167
|
-
`);
|
|
168
|
-
})
|
|
169
|
-
|
|
170
|
-
test('remove tag on external field if found on definition', () => {
|
|
171
|
-
const s1 = `
|
|
172
|
-
type Query {
|
|
173
|
-
a: A @provides(fields: "y")
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
type A @key(fields: "id") {
|
|
177
|
-
id: String
|
|
178
|
-
x: Int
|
|
179
|
-
y: Int @external @tag(name: "a tag")
|
|
180
|
-
}
|
|
181
|
-
`;
|
|
182
|
-
|
|
183
|
-
const s2 = `
|
|
184
|
-
type A @key(fields: "id") {
|
|
185
|
-
id: String
|
|
186
|
-
y: Int @tag(name: "a tag")
|
|
187
|
-
}
|
|
188
|
-
`;
|
|
189
|
-
|
|
190
|
-
const subgraphs = new Subgraphs();
|
|
191
|
-
subgraphs.add(buildSubgraph('s1', 'http://s1', s1));
|
|
192
|
-
subgraphs.add(buildSubgraph('s2', 'http://s2', s2));
|
|
193
|
-
const res = upgradeSubgraphsIfNecessary(subgraphs);
|
|
194
|
-
expect(res.errors).toBeUndefined();
|
|
195
|
-
|
|
196
|
-
expect(changeMessages(res, 's1', 'REMOVED_TAG_ON_EXTERNAL')).toStrictEqual([
|
|
197
|
-
'Removed @tag(name: "a tag") application on @external "A.y" as the @tag application is on another definition',
|
|
198
|
-
]);
|
|
199
|
-
|
|
200
|
-
const typeAInS1 = res.subgraphs?.get('s1')?.schema.type("A") as ObjectType;
|
|
201
|
-
const typeAInS2 = res.subgraphs?.get('s2')?.schema.type("A") as ObjectType;
|
|
202
|
-
expect(typeAInS1.field("y")?.appliedDirectivesOf('tag').map((d) => d.toString())).toStrictEqual([]);
|
|
203
|
-
expect(typeAInS2.field("y")?.appliedDirectivesOf('tag').map((d) => d.toString())).toStrictEqual([ '@tag(name: "a tag")' ]);
|
|
204
|
-
})
|
|
205
|
-
|
|
206
|
-
test('reject @interfaceObject usage if not all subgraphs are fed2', () => {
|
|
207
|
-
// Note that this test both validates the rejection of fed1 subgraph when @interfaceObject is used somewhere, but also
|
|
208
|
-
// illustrate why we do so: fed1 schema can use @key on interface for backward compatibility, but it is ignored and
|
|
209
|
-
// the schema upgrader removes them. Given that actual support for @key on interfaces is necesarry to make @interfaceObject
|
|
210
|
-
// work, it would be really confusing to not reject the example below right away, since it "looks" like it the @key on
|
|
211
|
-
// the interface in the 2nd subgraph should work, but it actually won't.
|
|
212
|
-
|
|
213
|
-
const s1 = `
|
|
214
|
-
extend schema
|
|
215
|
-
@link(url: "https://specs.apollo.dev/federation/v2.3", import: [ "@key", "@interfaceObject"])
|
|
216
|
-
|
|
217
|
-
type Query {
|
|
218
|
-
a: A
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
type A @key(fields: "id") @interfaceObject {
|
|
222
|
-
id: String
|
|
223
|
-
x: Int
|
|
224
|
-
}
|
|
225
|
-
`;
|
|
226
|
-
|
|
227
|
-
const s2 = `
|
|
228
|
-
interface A @key(fields: "id") {
|
|
229
|
-
id: String
|
|
230
|
-
y: Int
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
type X implements A @key(fields: "id") {
|
|
234
|
-
id: String
|
|
235
|
-
y: Int
|
|
236
|
-
}
|
|
237
|
-
`;
|
|
238
|
-
|
|
239
|
-
const subgraphs = new Subgraphs();
|
|
240
|
-
subgraphs.add(buildSubgraph('s1', 'http://s1', s1));
|
|
241
|
-
subgraphs.add(buildSubgraph('s2', 'http://s2', s2));
|
|
242
|
-
const res = upgradeSubgraphsIfNecessary(subgraphs);
|
|
243
|
-
expect(res.errors?.map((e) => e.message)).toStrictEqual([
|
|
244
|
-
'The @interfaceObject directive can only be used if all subgraphs have federation 2 subgraph schema (schema with a `@link` to "https://specs.apollo.dev/federation" version 2.0 or newer): '
|
|
245
|
-
+ '@interfaceObject is used in subgraph "s1" but subgraph "s2" is not a federation 2 subgraph schema.'
|
|
246
|
-
]);
|
|
247
|
-
})
|
|
248
|
-
|
|
249
|
-
test('handles the addition of @shareable when an @external is used on a type', () => {
|
|
250
|
-
const s1 = `
|
|
251
|
-
type Query {
|
|
252
|
-
t1: T
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
type T @key(fields: "id") {
|
|
256
|
-
id: String
|
|
257
|
-
x: Int
|
|
258
|
-
}
|
|
259
|
-
`;
|
|
260
|
-
|
|
261
|
-
const s2 = `
|
|
262
|
-
type Query {
|
|
263
|
-
t2: T
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
type T @external {
|
|
267
|
-
x: Int
|
|
268
|
-
}
|
|
269
|
-
`;
|
|
270
|
-
|
|
271
|
-
const subgraphs = new Subgraphs();
|
|
272
|
-
subgraphs.add(buildSubgraph('s1', 'http://s1', s1));
|
|
273
|
-
subgraphs.add(buildSubgraph('s2', 'http://s2', s2));
|
|
274
|
-
const res = upgradeSubgraphsIfNecessary(subgraphs);
|
|
275
|
-
expect(res.errors).toBeUndefined();
|
|
276
|
-
|
|
277
|
-
// 2 things must happen here:
|
|
278
|
-
// 1. the @external on type `T` in s2 should be removed, as @external on types were no-ops in fed1 (but not in fed2 anymore, hence the removal)
|
|
279
|
-
// 2. field `T.x` in s1 must be marked @shareable since it is resolved by s2 (since again, it's @external annotation is ignored).
|
|
280
|
-
|
|
281
|
-
const s2Upgraded = res.subgraphs?.get('s2')!;
|
|
282
|
-
expect(s2Upgraded.schema.type('T')?.hasAppliedDirective('external')).toBe(false);
|
|
283
|
-
|
|
284
|
-
const s1Upgraded = res.subgraphs?.get('s1')!;
|
|
285
|
-
expect((s1Upgraded.schema.type('T') as ObjectType).field('x')?.hasAppliedDirective('shareable')).toBe(true);
|
|
286
|
-
|
|
287
|
-
})
|