@apollo/federation-internals 2.0.0-alpha.6 → 2.0.0-preview.2
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/CHANGELOG.md +12 -0
- package/dist/buildSchema.d.ts +7 -3
- package/dist/buildSchema.d.ts.map +1 -1
- package/dist/buildSchema.js +41 -22
- package/dist/buildSchema.js.map +1 -1
- package/dist/coreSpec.d.ts +26 -4
- package/dist/coreSpec.d.ts.map +1 -1
- package/dist/coreSpec.js +86 -25
- package/dist/coreSpec.js.map +1 -1
- package/dist/definitions.d.ts +50 -43
- package/dist/definitions.d.ts.map +1 -1
- package/dist/definitions.js +201 -217
- package/dist/definitions.js.map +1 -1
- package/dist/directiveAndTypeSpecification.d.ts +38 -0
- package/dist/directiveAndTypeSpecification.d.ts.map +1 -0
- package/dist/directiveAndTypeSpecification.js +196 -0
- package/dist/directiveAndTypeSpecification.js.map +1 -0
- package/dist/error.d.ts +9 -1
- package/dist/error.d.ts.map +1 -1
- package/dist/error.js +20 -2
- package/dist/error.js.map +1 -1
- package/dist/extractSubgraphsFromSupergraph.d.ts.map +1 -1
- package/dist/extractSubgraphsFromSupergraph.js +28 -93
- package/dist/extractSubgraphsFromSupergraph.js.map +1 -1
- package/dist/federation.d.ts +88 -46
- package/dist/federation.d.ts.map +1 -1
- package/dist/federation.js +706 -228
- package/dist/federation.js.map +1 -1
- package/dist/federationSpec.d.ts +19 -0
- package/dist/federationSpec.d.ts.map +1 -0
- package/dist/federationSpec.js +91 -0
- package/dist/federationSpec.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -1
- package/dist/index.js.map +1 -1
- package/dist/joinSpec.d.ts +1 -0
- package/dist/joinSpec.d.ts.map +1 -1
- package/dist/joinSpec.js +1 -0
- package/dist/joinSpec.js.map +1 -1
- package/dist/operations.d.ts +8 -1
- package/dist/operations.d.ts.map +1 -1
- package/dist/operations.js +11 -4
- package/dist/operations.js.map +1 -1
- package/dist/print.d.ts +11 -9
- package/dist/print.d.ts.map +1 -1
- package/dist/print.js +21 -11
- package/dist/print.js.map +1 -1
- package/dist/schemaUpgrader.d.ts +108 -0
- package/dist/schemaUpgrader.d.ts.map +1 -0
- package/dist/schemaUpgrader.js +498 -0
- package/dist/schemaUpgrader.js.map +1 -0
- package/dist/sharing.d.ts +3 -0
- package/dist/sharing.d.ts.map +1 -0
- package/dist/sharing.js +51 -0
- package/dist/sharing.js.map +1 -0
- package/dist/supergraphs.d.ts.map +1 -1
- package/dist/supergraphs.js +2 -3
- package/dist/supergraphs.js.map +1 -1
- package/dist/tagSpec.d.ts.map +1 -1
- package/dist/tagSpec.js +1 -3
- package/dist/tagSpec.js.map +1 -1
- package/dist/utils.d.ts +7 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +34 -1
- package/dist/utils.js.map +1 -1
- package/dist/validate.d.ts.map +1 -1
- package/dist/validate.js +9 -4
- package/dist/validate.js.map +1 -1
- package/dist/validation/KnownTypeNamesInFederationRule.d.ts.map +1 -1
- package/dist/validation/KnownTypeNamesInFederationRule.js +1 -2
- package/dist/validation/KnownTypeNamesInFederationRule.js.map +1 -1
- package/dist/values.d.ts +1 -0
- package/dist/values.d.ts.map +1 -1
- package/dist/values.js +3 -2
- package/dist/values.js.map +1 -1
- package/package.json +3 -3
- package/src/__tests__/definitions.test.ts +19 -17
- package/src/__tests__/federation.test.ts +31 -0
- package/src/__tests__/operations.test.ts +2 -3
- package/src/__tests__/schemaUpgrader.test.ts +168 -0
- package/src/__tests__/subgraphValidation.test.ts +5 -19
- package/src/__tests__/values.test.ts +2 -4
- package/src/buildSchema.ts +55 -36
- package/src/coreSpec.ts +112 -31
- package/src/definitions.ts +247 -260
- package/src/directiveAndTypeSpecification.ts +276 -0
- package/src/error.ts +55 -5
- package/src/extractSubgraphsFromSupergraph.ts +34 -118
- package/src/federation.ts +912 -295
- package/src/federationSpec.ts +113 -0
- package/src/index.ts +2 -0
- package/src/joinSpec.ts +2 -1
- package/src/operations.ts +22 -7
- package/src/print.ts +51 -38
- package/src/schemaUpgrader.ts +657 -0
- package/src/sharing.ts +68 -0
- package/src/supergraphs.ts +3 -3
- package/src/tagSpec.ts +1 -3
- package/src/utils.ts +63 -0
- package/src/validate.ts +13 -7
- package/src/validation/KnownTypeNamesInFederationRule.ts +1 -7
- package/src/values.ts +7 -3
- package/tsconfig.test.tsbuildinfo +1 -1
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { buildSubgraph, Subgraphs } from '../federation';
|
|
2
|
+
import { UpgradeChangeID, UpgradeResult, upgradeSubgraphsIfNecessary } from '../schemaUpgrader';
|
|
3
|
+
import './matchers';
|
|
4
|
+
|
|
5
|
+
function changeMessages(res: UpgradeResult, subgraphName: string, id: UpgradeChangeID): string[] {
|
|
6
|
+
const changes = res.changes?.get(subgraphName)?.get(id);
|
|
7
|
+
return changes?.map(c => c.toString()) ?? [];
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* A lot of the schema upgrader behaviors are tested as part of composing fed1 schema in `composeFed1Subgraphs.test.ts`.
|
|
12
|
+
* This test file thus mostly focuses on the change-reporting of the schema upgrader.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
test('upgrade complex schema', () => {
|
|
16
|
+
const s1 = `
|
|
17
|
+
type Query {
|
|
18
|
+
products: [Product!]! @provides(fields: "upc description")
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface I @key(fields: "upc") {
|
|
22
|
+
upc: ID!
|
|
23
|
+
description: String @external
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
extend type Product implements I @key(fields: "upc") {
|
|
27
|
+
upc: ID! @external
|
|
28
|
+
name: String @external
|
|
29
|
+
inventory: Int @requires(fields: "upc")
|
|
30
|
+
description: String @external
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
# A type with a genuine 'graphqQL' extension, to ensure the extend don't get removed.
|
|
34
|
+
type Random {
|
|
35
|
+
x: Int @provides(fields: "x")
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
extend type Random {
|
|
39
|
+
y: Int
|
|
40
|
+
}
|
|
41
|
+
`;
|
|
42
|
+
|
|
43
|
+
// Note that no changes are really expected on that 2nd schema: it is just there to make the example not throw due to
|
|
44
|
+
// then Product type extension having no "base".
|
|
45
|
+
const s2 = `
|
|
46
|
+
type Product @key(fields: "upc") {
|
|
47
|
+
upc: ID!
|
|
48
|
+
name: String
|
|
49
|
+
description: String
|
|
50
|
+
}
|
|
51
|
+
`;
|
|
52
|
+
|
|
53
|
+
const subgraphs = new Subgraphs();
|
|
54
|
+
subgraphs.add(buildSubgraph('s1', 'http://s1', s1));
|
|
55
|
+
subgraphs.add(buildSubgraph('s2', 'http://s1', s2));
|
|
56
|
+
const res = upgradeSubgraphsIfNecessary(subgraphs);
|
|
57
|
+
expect(res.errors).toBeUndefined();
|
|
58
|
+
|
|
59
|
+
expect(changeMessages(res, 's1', 'EXTERNAL_ON_TYPE_EXTENSION_REMOVAL')).toStrictEqual([
|
|
60
|
+
'Removed @external from field "Product.upc" as it is a key of an extension type'
|
|
61
|
+
]);
|
|
62
|
+
|
|
63
|
+
expect(changeMessages(res, 's1', 'TYPE_EXTENSION_REMOVAL')).toStrictEqual([
|
|
64
|
+
'Switched type "Product" from an extension to a definition'
|
|
65
|
+
]);
|
|
66
|
+
|
|
67
|
+
expect(changeMessages(res, 's1', 'UNUSED_EXTERNAL_REMOVAL')).toStrictEqual([
|
|
68
|
+
'Removed @external field "Product.name" as it was not used in any @key, @provides or @requires'
|
|
69
|
+
]);
|
|
70
|
+
|
|
71
|
+
expect(changeMessages(res, 's1', 'EXTERNAL_ON_INTERFACE_REMOVAL')).toStrictEqual([
|
|
72
|
+
'Removed @external directive on interface type field "I.description": @external is nonsensical on interface fields'
|
|
73
|
+
]);
|
|
74
|
+
|
|
75
|
+
expect(changeMessages(res, 's1', 'INACTIVE_PROVIDES_OR_REQUIRES_REMOVAL')).toStrictEqual([
|
|
76
|
+
'Removed directive @requires(fields: "upc") on "Product.inventory": none of the fields were truly @external'
|
|
77
|
+
]);
|
|
78
|
+
|
|
79
|
+
expect(changeMessages(res, 's1', 'INACTIVE_PROVIDES_OR_REQUIRES_FIELDS_REMOVAL')).toStrictEqual([
|
|
80
|
+
'Updated directive @provides(fields: "upc description") on "Query.products" to @provides(fields: "description"): removed fields that were not truly @external'
|
|
81
|
+
]);
|
|
82
|
+
|
|
83
|
+
expect(changeMessages(res, 's1', 'KEY_ON_INTERFACE_REMOVAL')).toStrictEqual([
|
|
84
|
+
'Removed @key on interface "I": while allowed by federation 0.x, @key on interfaces were completely ignored/had no effect'
|
|
85
|
+
]);
|
|
86
|
+
|
|
87
|
+
expect(changeMessages(res, 's1', 'PROVIDES_ON_NON_COMPOSITE_REMOVAL')).toStrictEqual([
|
|
88
|
+
'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'
|
|
89
|
+
]);
|
|
90
|
+
|
|
91
|
+
expect(res.subgraphs?.get('s1')?.toString()).toMatchString(`
|
|
92
|
+
schema
|
|
93
|
+
@link(url: "https://specs.apollo.dev/link/v1.0")
|
|
94
|
+
@link(url: "https://specs.apollo.dev/federation/v2.0", import: ["@key", "@requires", "@provides", "@external", "@shareable", "@tag", "@extends"])
|
|
95
|
+
{
|
|
96
|
+
query: Query
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
type Query {
|
|
100
|
+
products: [Product!]! @provides(fields: "description")
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
interface I {
|
|
104
|
+
upc: ID!
|
|
105
|
+
description: String
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
type Product implements I
|
|
109
|
+
@key(fields: "upc")
|
|
110
|
+
{
|
|
111
|
+
upc: ID!
|
|
112
|
+
inventory: Int
|
|
113
|
+
description: String @external
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
type Random {
|
|
117
|
+
x: Int
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
extend type Random {
|
|
121
|
+
y: Int
|
|
122
|
+
}
|
|
123
|
+
`);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
test('update federation directive non-string arguments', () => {
|
|
127
|
+
const s = `
|
|
128
|
+
type Query {
|
|
129
|
+
a: A
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
type A @key(fields: id) @key(fields: ["id", "x"]) {
|
|
133
|
+
id: String
|
|
134
|
+
x: Int
|
|
135
|
+
}
|
|
136
|
+
`;
|
|
137
|
+
|
|
138
|
+
const subgraphs = new Subgraphs();
|
|
139
|
+
subgraphs.add(buildSubgraph('s', 'http://s', s));
|
|
140
|
+
const res = upgradeSubgraphsIfNecessary(subgraphs);
|
|
141
|
+
expect(res.errors).toBeUndefined();
|
|
142
|
+
|
|
143
|
+
expect(changeMessages(res, 's', 'FIELDS_ARGUMENT_COERCION_TO_STRING')).toStrictEqual([
|
|
144
|
+
'Coerced "fields" argument for directive @key for "A" into a string: coerced from @key(fields: id) to @key(fields: "id")',
|
|
145
|
+
'Coerced "fields" argument for directive @key for "A" into a string: coerced from @key(fields: ["id", "x"]) to @key(fields: "id x")',
|
|
146
|
+
]);
|
|
147
|
+
|
|
148
|
+
expect(res.subgraphs?.get('s')?.toString()).toMatchString(`
|
|
149
|
+
schema
|
|
150
|
+
@link(url: "https://specs.apollo.dev/link/v1.0")
|
|
151
|
+
@link(url: "https://specs.apollo.dev/federation/v2.0", import: ["@key", "@requires", "@provides", "@external", "@shareable", "@tag", "@extends"])
|
|
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
|
+
})
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import { DocumentNode } from 'graphql';
|
|
2
2
|
import gql from 'graphql-tag';
|
|
3
|
-
import { errorCauses } from '
|
|
4
|
-
import { buildSubgraph } from "../federation"
|
|
3
|
+
import { errorCauses } from '../definitions';
|
|
4
|
+
import { asFed2SubgraphDocument, buildSubgraph } from "../federation"
|
|
5
5
|
|
|
6
6
|
// Builds the provided subgraph (using name 'S' for the subgraph) and, if the
|
|
7
7
|
// subgraph is invalid/has errors, return those errors as a list of [code, message].
|
|
8
8
|
// If the subgraph is valid, return undefined.
|
|
9
9
|
function buildForErrors(subgraphDefs: DocumentNode, subgraphName: string = 'S'): [string, string][] | undefined {
|
|
10
10
|
try {
|
|
11
|
-
|
|
11
|
+
const doc = asFed2SubgraphDocument(subgraphDefs);
|
|
12
|
+
buildSubgraph(subgraphName, `http://${subgraphName}`, doc).validate();
|
|
12
13
|
return undefined;
|
|
13
14
|
} catch (e) {
|
|
14
15
|
const causes = errorCauses(e);
|
|
@@ -144,6 +145,7 @@ describe('fieldset-based directives', () => {
|
|
|
144
145
|
`
|
|
145
146
|
expect(buildForErrors(subgraph)).toStrictEqual([
|
|
146
147
|
['REQUIRES_UNSUPPORTED_ON_INTERFACE', '[S] Cannot use @requires on field "T.g" of parent type "T": @requires is not yet supported within interfaces' ],
|
|
148
|
+
['EXTERNAL_ON_INTERFACE', '[S] Interface type field "T.f" is marked @external but @external is not allowed on interface fields (it is nonsensical).' ],
|
|
147
149
|
]);
|
|
148
150
|
});
|
|
149
151
|
|
|
@@ -335,22 +337,6 @@ describe('fieldset-based directives', () => {
|
|
|
335
337
|
`
|
|
336
338
|
expect(buildForErrors(subgraph)).toStrictEqual([
|
|
337
339
|
['REQUIRES_INVALID_FIELDS', '[S] On field "T.g", for @requires(fields: "f b"): Cannot query field "b" on type "T" (if the field is defined in another subgraph, you need to add it to this subgraph with @external).'],
|
|
338
|
-
['EXTERNAL_UNUSED', '[S] Field "T.f" is marked @external but is not used in any federation directive (@key, @provides, @requires) or to satisfy an interface; the field declaration has no use and should be removed (or the field should not be @external).' ],
|
|
339
|
-
]);
|
|
340
|
-
});
|
|
341
|
-
|
|
342
|
-
it('rejects @key on a list field', () => {
|
|
343
|
-
const subgraph = gql`
|
|
344
|
-
type Query {
|
|
345
|
-
t: T
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
type T @key(fields: "f") {
|
|
349
|
-
f: [Int]
|
|
350
|
-
}
|
|
351
|
-
`
|
|
352
|
-
expect(buildForErrors(subgraph)).toStrictEqual([
|
|
353
|
-
['KEY_FIELDS_SELECT_INVALID_TYPE', '[S] On type "T", for @key(fields: "f"): field "T.f" is a List type which is not allowed in @key'],
|
|
354
340
|
]);
|
|
355
341
|
});
|
|
356
342
|
|
|
@@ -1,19 +1,17 @@
|
|
|
1
1
|
import {
|
|
2
2
|
Schema,
|
|
3
|
-
BuiltIns
|
|
4
3
|
} from '../../dist/definitions';
|
|
5
4
|
import { buildSchema } from '../../dist/buildSchema';
|
|
6
5
|
import { parseOperation } from '../../dist/operations';
|
|
7
6
|
|
|
8
|
-
function parseSchema(schema: string
|
|
7
|
+
function parseSchema(schema: string): Schema {
|
|
9
8
|
try {
|
|
10
|
-
return buildSchema(schema
|
|
9
|
+
return buildSchema(schema);
|
|
11
10
|
} catch (e) {
|
|
12
11
|
throw new Error('Error parsing the schema:\n' + e.toString());
|
|
13
12
|
}
|
|
14
13
|
}
|
|
15
14
|
|
|
16
|
-
|
|
17
15
|
test('handles non-list value for list argument (as singleton)', () => {
|
|
18
16
|
const schema = parseSchema(`
|
|
19
17
|
enum Day {
|
package/src/buildSchema.ts
CHANGED
|
@@ -11,7 +11,6 @@ import {
|
|
|
11
11
|
SchemaDefinitionNode,
|
|
12
12
|
Source,
|
|
13
13
|
TypeNode,
|
|
14
|
-
valueFromASTUntyped,
|
|
15
14
|
ValueNode,
|
|
16
15
|
NamedTypeNode,
|
|
17
16
|
ArgumentNode,
|
|
@@ -22,10 +21,10 @@ import {
|
|
|
22
21
|
Kind,
|
|
23
22
|
} from "graphql";
|
|
24
23
|
import { Maybe } from "graphql/jsutils/Maybe";
|
|
24
|
+
import { valueFromASTUntyped } from "./values";
|
|
25
25
|
import {
|
|
26
|
-
|
|
26
|
+
SchemaBlueprint,
|
|
27
27
|
Schema,
|
|
28
|
-
graphQLBuiltIns,
|
|
29
28
|
newNamedType,
|
|
30
29
|
NamedTypeKind,
|
|
31
30
|
NamedType,
|
|
@@ -51,57 +50,56 @@ import {
|
|
|
51
50
|
} from "./definitions";
|
|
52
51
|
|
|
53
52
|
function buildValue(value?: ValueNode): any {
|
|
54
|
-
// TODO: Should we rewrite a version of valueFromAST instead of using valueFromASTUntyped? Afaict, what we're missing out on is
|
|
55
|
-
// 1) coercions, which concretely, means:
|
|
56
|
-
// - for enums, we get strings
|
|
57
|
-
// - for int, we don't get the validation that it should be a 32bit value.
|
|
58
|
-
// - for ID, which accepts strings and int, we don't get int converted to string.
|
|
59
|
-
// - for floats, we get either int or float, we don't get int converted to float.
|
|
60
|
-
// - we don't get any custom coercion (but neither is buildSchema in graphQL-js anyway).
|
|
61
|
-
// 2) type validation.
|
|
62
53
|
return value ? valueFromASTUntyped(value) : undefined;
|
|
63
54
|
}
|
|
64
55
|
|
|
65
|
-
export
|
|
66
|
-
|
|
56
|
+
export type BuildSchemaOptions = {
|
|
57
|
+
blueprint?: SchemaBlueprint,
|
|
58
|
+
validate?: boolean,
|
|
67
59
|
}
|
|
68
60
|
|
|
69
|
-
export function
|
|
70
|
-
|
|
61
|
+
export function buildSchema(source: string | Source, options?: BuildSchemaOptions): Schema {
|
|
62
|
+
return buildSchemaFromAST(parse(source), options);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function buildSchemaFromAST(
|
|
66
|
+
documentNode: DocumentNode,
|
|
67
|
+
options?: BuildSchemaOptions,
|
|
68
|
+
): Schema {
|
|
69
|
+
const schema = new Schema(options?.blueprint);
|
|
71
70
|
// We do a first pass to add all empty types and directives definition. This ensure any reference on one of
|
|
72
71
|
// those can be resolved in the 2nd pass, regardless of the order of the definitions in the AST.
|
|
73
|
-
const
|
|
72
|
+
const { directiveDefinitions, schemaDefinitions, schemaExtensions } = buildNamedTypeAndDirectivesShallow(documentNode, schema);
|
|
74
73
|
|
|
75
74
|
// We then deal with directive definition first. This is mainly for the sake of core schemas: the core schema
|
|
76
75
|
// handling in `Schema` detects that the schema is a core one when it see the application of `@core(feature: ".../core/...")`
|
|
77
76
|
// to the schema element. But that detection necessitates that the corresponding directive definition has been fully
|
|
78
77
|
// populated (and at this point, we don't really know the name of the `@core` directive since it can be renamed, so
|
|
79
78
|
// we just handle all directives).
|
|
80
|
-
for (const directiveDefinitionNode of
|
|
79
|
+
for (const directiveDefinitionNode of directiveDefinitions) {
|
|
81
80
|
buildDirectiveDefinitionInner(directiveDefinitionNode, schema.directive(directiveDefinitionNode.name.value)!);
|
|
82
81
|
}
|
|
82
|
+
for (const schemaDefinition of schemaDefinitions) {
|
|
83
|
+
buildSchemaDefinitionInner(schemaDefinition, schema.schemaDefinition);
|
|
84
|
+
}
|
|
85
|
+
for (const schemaExtension of schemaExtensions) {
|
|
86
|
+
buildSchemaDefinitionInner(schemaExtension, schema.schemaDefinition, schema.schemaDefinition.newExtension());
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
schema.blueprint.onDirectiveDefinitionAndSchemaParsed(schema);
|
|
83
90
|
|
|
84
91
|
for (const definitionNode of documentNode.definitions) {
|
|
85
92
|
switch (definitionNode.kind) {
|
|
86
93
|
case 'OperationDefinition':
|
|
87
94
|
case 'FragmentDefinition':
|
|
88
95
|
throw new GraphQLError("Invalid executable definition found while building schema", definitionNode);
|
|
89
|
-
case 'SchemaDefinition':
|
|
90
|
-
buildSchemaDefinitionInner(definitionNode, schema.schemaDefinition);
|
|
91
|
-
break;
|
|
92
|
-
case 'SchemaExtension':
|
|
93
|
-
buildSchemaDefinitionInner(
|
|
94
|
-
definitionNode,
|
|
95
|
-
schema.schemaDefinition,
|
|
96
|
-
schema.schemaDefinition.newExtension());
|
|
97
|
-
break;
|
|
98
96
|
case 'ScalarTypeDefinition':
|
|
99
97
|
case 'ObjectTypeDefinition':
|
|
100
98
|
case 'InterfaceTypeDefinition':
|
|
101
99
|
case 'UnionTypeDefinition':
|
|
102
100
|
case 'EnumTypeDefinition':
|
|
103
101
|
case 'InputObjectTypeDefinition':
|
|
104
|
-
buildNamedTypeInner(definitionNode, schema.type(definitionNode.name.value)
|
|
102
|
+
buildNamedTypeInner(definitionNode, schema.type(definitionNode.name.value)!, schema.blueprint);
|
|
105
103
|
break;
|
|
106
104
|
case 'ScalarTypeExtension':
|
|
107
105
|
case 'ObjectTypeExtension':
|
|
@@ -112,23 +110,34 @@ export function buildSchemaFromAST(documentNode: DocumentNode, builtIns: BuiltIn
|
|
|
112
110
|
const toExtend = schema.type(definitionNode.name.value)!;
|
|
113
111
|
const extension = toExtend.newExtension();
|
|
114
112
|
extension.sourceAST = definitionNode;
|
|
115
|
-
buildNamedTypeInner(definitionNode, toExtend, extension);
|
|
113
|
+
buildNamedTypeInner(definitionNode, toExtend, schema.blueprint, extension);
|
|
116
114
|
break;
|
|
117
115
|
}
|
|
118
116
|
}
|
|
119
117
|
|
|
120
|
-
|
|
121
|
-
if (validate) {
|
|
118
|
+
if (options?.validate ?? true) {
|
|
122
119
|
schema.validate();
|
|
123
120
|
}
|
|
124
121
|
|
|
125
122
|
return schema;
|
|
126
123
|
}
|
|
127
124
|
|
|
128
|
-
function buildNamedTypeAndDirectivesShallow(documentNode: DocumentNode, schema: Schema):
|
|
129
|
-
|
|
125
|
+
function buildNamedTypeAndDirectivesShallow(documentNode: DocumentNode, schema: Schema): {
|
|
126
|
+
directiveDefinitions: DirectiveDefinitionNode[],
|
|
127
|
+
schemaDefinitions: SchemaDefinitionNode[],
|
|
128
|
+
schemaExtensions: SchemaExtensionNode[],
|
|
129
|
+
} {
|
|
130
|
+
const directiveDefinitions = [];
|
|
131
|
+
const schemaDefinitions = [];
|
|
132
|
+
const schemaExtensions = [];
|
|
130
133
|
for (const definitionNode of documentNode.definitions) {
|
|
131
134
|
switch (definitionNode.kind) {
|
|
135
|
+
case 'SchemaDefinition':
|
|
136
|
+
schemaDefinitions.push(definitionNode);
|
|
137
|
+
break;
|
|
138
|
+
case 'SchemaExtension':
|
|
139
|
+
schemaExtensions.push(definitionNode);
|
|
140
|
+
break;
|
|
132
141
|
case 'ScalarTypeDefinition':
|
|
133
142
|
case 'ObjectTypeDefinition':
|
|
134
143
|
case 'InterfaceTypeDefinition':
|
|
@@ -149,12 +158,16 @@ function buildNamedTypeAndDirectivesShallow(documentNode: DocumentNode, schema:
|
|
|
149
158
|
}
|
|
150
159
|
break;
|
|
151
160
|
case 'DirectiveDefinition':
|
|
152
|
-
|
|
161
|
+
directiveDefinitions.push(definitionNode);
|
|
153
162
|
schema.addDirectiveDefinition(definitionNode.name.value);
|
|
154
163
|
break;
|
|
155
164
|
}
|
|
156
165
|
}
|
|
157
|
-
return
|
|
166
|
+
return {
|
|
167
|
+
directiveDefinitions,
|
|
168
|
+
schemaDefinitions,
|
|
169
|
+
schemaExtensions,
|
|
170
|
+
}
|
|
158
171
|
}
|
|
159
172
|
|
|
160
173
|
type NodeWithDirectives = {directives?: ReadonlyArray<DirectiveNode>};
|
|
@@ -206,7 +219,9 @@ function buildSchemaDefinitionInner(
|
|
|
206
219
|
opTypeNode);
|
|
207
220
|
}
|
|
208
221
|
schemaDefinition.sourceAST = schemaNode;
|
|
209
|
-
|
|
222
|
+
if ('description' in schemaNode) {
|
|
223
|
+
schemaDefinition.description = schemaNode.description?.value;
|
|
224
|
+
}
|
|
210
225
|
buildAppliedDirectives(schemaNode, schemaDefinition, extension);
|
|
211
226
|
}
|
|
212
227
|
|
|
@@ -235,7 +250,8 @@ function buildArgs(argumentsNode: NodeWithArguments): Record<string, any> {
|
|
|
235
250
|
function buildNamedTypeInner(
|
|
236
251
|
definitionNode: DefinitionNode & NodeWithDirectives & NodeWithDescription,
|
|
237
252
|
type: NamedType,
|
|
238
|
-
|
|
253
|
+
blueprint: SchemaBlueprint,
|
|
254
|
+
extension?: Extension<any>,
|
|
239
255
|
) {
|
|
240
256
|
switch (definitionNode.kind) {
|
|
241
257
|
case 'ObjectTypeDefinition':
|
|
@@ -244,6 +260,9 @@ function buildNamedTypeInner(
|
|
|
244
260
|
case 'InterfaceTypeExtension':
|
|
245
261
|
const fieldBasedType = type as ObjectType | InterfaceType;
|
|
246
262
|
for (const fieldNode of definitionNode.fields ?? []) {
|
|
263
|
+
if (blueprint.ignoreParsedField(type, fieldNode.name.value)) {
|
|
264
|
+
continue;
|
|
265
|
+
}
|
|
247
266
|
const field = fieldBasedType.addField(fieldNode.name.value);
|
|
248
267
|
field.setOfExtension(extension);
|
|
249
268
|
buildFieldDefinitionInner(fieldNode, field);
|