@apollo/federation-internals 2.0.0-preview.1 → 2.0.0-preview.10
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 +35 -3
- package/dist/buildSchema.d.ts.map +1 -1
- package/dist/buildSchema.js +59 -45
- package/dist/buildSchema.js.map +1 -1
- package/dist/coreSpec.d.ts +16 -8
- package/dist/coreSpec.d.ts.map +1 -1
- package/dist/coreSpec.js +171 -42
- package/dist/coreSpec.js.map +1 -1
- package/dist/definitions.d.ts +24 -11
- package/dist/definitions.d.ts.map +1 -1
- package/dist/definitions.js +142 -31
- package/dist/definitions.js.map +1 -1
- package/dist/directiveAndTypeSpecification.d.ts +11 -1
- package/dist/directiveAndTypeSpecification.d.ts.map +1 -1
- package/dist/directiveAndTypeSpecification.js +77 -20
- package/dist/directiveAndTypeSpecification.js.map +1 -1
- package/dist/error.d.ts +12 -0
- package/dist/error.d.ts.map +1 -1
- package/dist/error.js +44 -2
- package/dist/error.js.map +1 -1
- package/dist/extractSubgraphsFromSupergraph.d.ts.map +1 -1
- package/dist/extractSubgraphsFromSupergraph.js +15 -5
- package/dist/extractSubgraphsFromSupergraph.js.map +1 -1
- package/dist/federation.d.ts +18 -4
- package/dist/federation.d.ts.map +1 -1
- package/dist/federation.js +117 -67
- package/dist/federation.js.map +1 -1
- package/dist/federationSpec.d.ts +6 -2
- package/dist/federationSpec.d.ts.map +1 -1
- package/dist/federationSpec.js +47 -21
- package/dist/federationSpec.js.map +1 -1
- package/dist/inaccessibleSpec.d.ts +5 -1
- package/dist/inaccessibleSpec.d.ts.map +1 -1
- package/dist/inaccessibleSpec.js +31 -3
- package/dist/inaccessibleSpec.js.map +1 -1
- 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/introspection.d.ts.map +1 -1
- package/dist/introspection.js +8 -3
- package/dist/introspection.js.map +1 -1
- package/dist/joinSpec.d.ts +5 -1
- package/dist/joinSpec.d.ts.map +1 -1
- package/dist/joinSpec.js +21 -0
- package/dist/joinSpec.js.map +1 -1
- package/dist/knownCoreFeatures.d.ts +4 -0
- package/dist/knownCoreFeatures.d.ts.map +1 -0
- package/dist/knownCoreFeatures.js +16 -0
- package/dist/knownCoreFeatures.js.map +1 -0
- package/dist/operations.d.ts +1 -0
- package/dist/operations.d.ts.map +1 -1
- package/dist/operations.js +16 -1
- package/dist/operations.js.map +1 -1
- package/dist/{sharing.d.ts → precompute.d.ts} +1 -1
- package/dist/precompute.d.ts.map +1 -0
- package/dist/{sharing.js → precompute.js} +3 -3
- package/dist/precompute.js.map +1 -0
- package/dist/print.js +12 -12
- package/dist/print.js.map +1 -1
- package/dist/schemaUpgrader.d.ts.map +1 -1
- package/dist/schemaUpgrader.js +5 -6
- package/dist/schemaUpgrader.js.map +1 -1
- package/dist/suggestions.d.ts +1 -1
- package/dist/suggestions.d.ts.map +1 -1
- package/dist/suggestions.js.map +1 -1
- package/dist/supergraphs.d.ts.map +1 -1
- package/dist/supergraphs.js +1 -0
- package/dist/supergraphs.js.map +1 -1
- package/dist/tagSpec.d.ts +7 -2
- package/dist/tagSpec.d.ts.map +1 -1
- package/dist/tagSpec.js +35 -13
- package/dist/tagSpec.js.map +1 -1
- package/dist/validate.js +10 -7
- package/dist/validate.js.map +1 -1
- package/package.json +3 -3
- package/src/__tests__/coreSpec.test.ts +100 -0
- package/src/__tests__/definitions.test.ts +79 -0
- package/src/__tests__/extractSubgraphsFromSupergraph.test.ts +64 -0
- package/src/__tests__/removeInaccessibleElements.test.ts +59 -6
- package/src/__tests__/schemaUpgrader.test.ts +3 -2
- package/src/__tests__/subgraphValidation.test.ts +419 -4
- package/src/buildSchema.ts +104 -53
- package/src/coreSpec.ts +208 -50
- package/src/definitions.ts +185 -32
- package/src/directiveAndTypeSpecification.ts +98 -21
- package/src/error.ts +89 -1
- package/src/extractSubgraphsFromSupergraph.ts +23 -5
- package/src/federation.ts +154 -82
- package/src/federationSpec.ts +56 -23
- package/src/inaccessibleSpec.ts +39 -11
- package/src/index.ts +2 -0
- package/src/introspection.ts +8 -3
- package/src/joinSpec.ts +33 -3
- package/src/knownCoreFeatures.ts +13 -0
- package/src/operations.ts +15 -0
- package/src/{sharing.ts → precompute.ts} +3 -6
- package/src/print.ts +13 -11
- package/src/schemaUpgrader.ts +8 -12
- package/src/suggestions.ts +1 -1
- package/src/supergraphs.ts +1 -0
- package/src/tagSpec.ts +49 -15
- package/src/validate.ts +14 -9
- package/tsconfig.test.tsbuildinfo +1 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/sharing.d.ts.map +0 -1
- package/dist/sharing.js.map +0 -1
package/src/inaccessibleSpec.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { FeatureDefinition, FeatureDefinitions, FeatureUrl, FeatureVersion } from "./coreSpec";
|
|
2
2
|
import {
|
|
3
3
|
DirectiveDefinition,
|
|
4
|
+
ErrGraphQLValidationFailed,
|
|
4
5
|
FieldDefinition,
|
|
5
6
|
isCompositeType,
|
|
6
7
|
isInterfaceType,
|
|
@@ -8,31 +9,47 @@ import {
|
|
|
8
9
|
Schema,
|
|
9
10
|
} from "./definitions";
|
|
10
11
|
import { GraphQLError, DirectiveLocation } from "graphql";
|
|
12
|
+
import { registerKnownFeature } from "./knownCoreFeatures";
|
|
13
|
+
import { ERRORS } from "./error";
|
|
14
|
+
import { createDirectiveSpecification } from "./directiveAndTypeSpecification";
|
|
11
15
|
|
|
12
16
|
export const inaccessibleIdentity = 'https://specs.apollo.dev/inaccessible';
|
|
13
17
|
|
|
18
|
+
export const inaccessibleLocations = [
|
|
19
|
+
DirectiveLocation.FIELD_DEFINITION,
|
|
20
|
+
DirectiveLocation.OBJECT,
|
|
21
|
+
DirectiveLocation.INTERFACE,
|
|
22
|
+
DirectiveLocation.UNION,
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
export const inaccessibleDirectiveSpec = createDirectiveSpecification({
|
|
26
|
+
name: 'inaccessible',
|
|
27
|
+
locations: [...inaccessibleLocations],
|
|
28
|
+
});
|
|
29
|
+
|
|
14
30
|
export class InaccessibleSpecDefinition extends FeatureDefinition {
|
|
15
31
|
constructor(version: FeatureVersion) {
|
|
16
32
|
super(new FeatureUrl(inaccessibleIdentity, 'inaccessible', version));
|
|
17
33
|
}
|
|
18
34
|
|
|
19
|
-
addElementsToSchema(schema: Schema) {
|
|
20
|
-
this.
|
|
21
|
-
DirectiveLocation.FIELD_DEFINITION,
|
|
22
|
-
DirectiveLocation.OBJECT,
|
|
23
|
-
DirectiveLocation.INTERFACE,
|
|
24
|
-
DirectiveLocation.UNION,
|
|
25
|
-
);
|
|
35
|
+
addElementsToSchema(schema: Schema): GraphQLError[] {
|
|
36
|
+
return this.addDirectiveSpec(schema, inaccessibleDirectiveSpec);
|
|
26
37
|
}
|
|
27
38
|
|
|
28
39
|
inaccessibleDirective(schema: Schema): DirectiveDefinition<Record<string, never>> {
|
|
29
40
|
return this.directive(schema, 'inaccessible')!;
|
|
30
41
|
}
|
|
42
|
+
|
|
43
|
+
allElementNames(): string[] {
|
|
44
|
+
return ['@inaccessible'];
|
|
45
|
+
}
|
|
31
46
|
}
|
|
32
47
|
|
|
33
48
|
export const INACCESSIBLE_VERSIONS = new FeatureDefinitions<InaccessibleSpecDefinition>(inaccessibleIdentity)
|
|
34
49
|
.add(new InaccessibleSpecDefinition(new FeatureVersion(0, 1)));
|
|
35
50
|
|
|
51
|
+
registerKnownFeature(INACCESSIBLE_VERSIONS);
|
|
52
|
+
|
|
36
53
|
export function removeInaccessibleElements(schema: Schema) {
|
|
37
54
|
const coreFeatures = schema.coreFeatures;
|
|
38
55
|
if (!coreFeatures) {
|
|
@@ -56,6 +73,7 @@ export function removeInaccessibleElements(schema: Schema) {
|
|
|
56
73
|
);
|
|
57
74
|
}
|
|
58
75
|
|
|
76
|
+
const errors = [];
|
|
59
77
|
for (const type of schema.types()) {
|
|
60
78
|
// @inaccessible can only be on composite types.
|
|
61
79
|
if (!isCompositeType(type)) {
|
|
@@ -69,10 +87,16 @@ export function removeInaccessibleElements(schema: Schema) {
|
|
|
69
87
|
// field type will be `undefined`).
|
|
70
88
|
if (reference.kind === 'FieldDefinition') {
|
|
71
89
|
if (!reference.hasAppliedDirective(inaccessibleDirective)) {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
90
|
+
// We ship the inaccessible type and it's invalid reference in the extensions so composition can extract those and add proper links
|
|
91
|
+
// in the subgraphs for those elements.
|
|
92
|
+
errors.push(ERRORS.REFERENCED_INACCESSIBLE.err({
|
|
93
|
+
message: `Field "${reference.coordinate}" returns @inaccessible type "${type}" without being marked @inaccessible itself.`,
|
|
94
|
+
nodes: reference.sourceAST,
|
|
95
|
+
extensions: {
|
|
96
|
+
"inaccessible_element": type.coordinate,
|
|
97
|
+
"inaccessible_reference": reference.coordinate,
|
|
98
|
+
}
|
|
99
|
+
}));
|
|
76
100
|
}
|
|
77
101
|
}
|
|
78
102
|
// Other references can be:
|
|
@@ -85,4 +109,8 @@ export function removeInaccessibleElements(schema: Schema) {
|
|
|
85
109
|
toRemove.forEach(f => f.remove());
|
|
86
110
|
}
|
|
87
111
|
}
|
|
112
|
+
|
|
113
|
+
if (errors.length > 0) {
|
|
114
|
+
throw ErrGraphQLValidationFailed(errors, `Schema has ${errors.length === 1 ? 'an invalid use' : 'invalid uses'} of the @inaccessible directive.`);
|
|
115
|
+
}
|
|
88
116
|
}
|
package/src/index.ts
CHANGED
|
@@ -10,8 +10,10 @@ export * from './debug';
|
|
|
10
10
|
export * from './coreSpec';
|
|
11
11
|
export * from './joinSpec';
|
|
12
12
|
export * from './tagSpec';
|
|
13
|
+
export * from './inaccessibleSpec';
|
|
13
14
|
export * from './federationSpec';
|
|
14
15
|
export * from './supergraphs';
|
|
15
16
|
export * from './extractSubgraphsFromSupergraph';
|
|
16
17
|
export * from './error';
|
|
17
18
|
export * from './schemaUpgrader';
|
|
19
|
+
export * from './suggestions';
|
package/src/introspection.ts
CHANGED
|
@@ -35,13 +35,15 @@ export function addIntrospectionFields(schema: Schema) {
|
|
|
35
35
|
typeType.addField('possibleTypes', new ListType(new NonNullType(typeType)));
|
|
36
36
|
typeType.addField('enumValues', new ListType(new NonNullType(enumValueType)))
|
|
37
37
|
.addArgument('includeDeprecated', schema.booleanType(), false);
|
|
38
|
-
typeType.addField('inputFields', new ListType(new NonNullType(inputValueType)))
|
|
38
|
+
typeType.addField('inputFields', new ListType(new NonNullType(inputValueType)))
|
|
39
|
+
.addArgument('includeDeprecated', schema.booleanType(), false);
|
|
39
40
|
typeType.addField('ofType', typeType);
|
|
40
41
|
typeType.addField('specifiedByURL', schema.stringType());
|
|
41
42
|
|
|
42
43
|
fieldType.addField('name', new NonNullType(schema.stringType()));
|
|
43
44
|
fieldType.addField('description', schema.stringType());
|
|
44
|
-
fieldType.addField('args', new NonNullType(new ListType(new NonNullType(inputValueType))))
|
|
45
|
+
fieldType.addField('args', new NonNullType(new ListType(new NonNullType(inputValueType))))
|
|
46
|
+
.addArgument('includeDeprecated', schema.booleanType(), false);
|
|
45
47
|
fieldType.addField('type', new NonNullType(typeType));
|
|
46
48
|
fieldType.addField('isDeprecated', new NonNullType(schema.booleanType()));
|
|
47
49
|
fieldType.addField('deprecationReason', schema.stringType());
|
|
@@ -50,6 +52,8 @@ export function addIntrospectionFields(schema: Schema) {
|
|
|
50
52
|
inputValueType.addField('description', schema.stringType());
|
|
51
53
|
inputValueType.addField('type', new NonNullType(typeType));
|
|
52
54
|
inputValueType.addField('defaultValue', schema.stringType());
|
|
55
|
+
inputValueType.addField('isDeprecated', new NonNullType(schema.booleanType()));
|
|
56
|
+
inputValueType.addField('deprecationReason', schema.stringType());
|
|
53
57
|
|
|
54
58
|
enumValueType.addField('name', new NonNullType(schema.stringType()));
|
|
55
59
|
enumValueType.addField('description', schema.stringType());
|
|
@@ -65,7 +69,8 @@ export function addIntrospectionFields(schema: Schema) {
|
|
|
65
69
|
directiveType.addField('name', new NonNullType(schema.stringType()));
|
|
66
70
|
directiveType.addField('description', schema.stringType());
|
|
67
71
|
directiveType.addField('locations', new NonNullType(new ListType(new NonNullType(directiveLocationEnum))));
|
|
68
|
-
directiveType.addField('args', new NonNullType(new ListType(new NonNullType(inputValueType))))
|
|
72
|
+
directiveType.addField('args', new NonNullType(new ListType(new NonNullType(inputValueType))))
|
|
73
|
+
.addArgument('includeDeprecated', schema.booleanType(), false);
|
|
69
74
|
directiveType.addField('isRepeatable', new NonNullType(schema.booleanType()));
|
|
70
75
|
|
|
71
76
|
const schemaType = schema.addType(new ObjectType('__Schema', true));
|
package/src/joinSpec.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DirectiveLocation } from 'graphql';
|
|
1
|
+
import { DirectiveLocation, GraphQLError } from 'graphql';
|
|
2
2
|
import { FeatureDefinition, FeatureDefinitions, FeatureUrl, FeatureVersion } from "./coreSpec";
|
|
3
3
|
import {
|
|
4
4
|
DirectiveDefinition,
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
NonNullType,
|
|
9
9
|
} from "./definitions";
|
|
10
10
|
import { Subgraph, Subgraphs } from "./federation";
|
|
11
|
+
import { registerKnownFeature } from './knownCoreFeatures';
|
|
11
12
|
import { MultiMap } from "./utils";
|
|
12
13
|
|
|
13
14
|
export const joinIdentity = 'https://specs.apollo.dev/join';
|
|
@@ -38,7 +39,7 @@ export class JoinSpecDefinition extends FeatureDefinition {
|
|
|
38
39
|
return this.version.equals(new FeatureVersion(0, 1));
|
|
39
40
|
}
|
|
40
41
|
|
|
41
|
-
addElementsToSchema(schema: Schema) {
|
|
42
|
+
addElementsToSchema(schema: Schema): GraphQLError[] {
|
|
42
43
|
const joinGraph = this.addDirective(schema, 'graph').addLocations(DirectiveLocation.ENUM_VALUE);
|
|
43
44
|
joinGraph.addArgument('name', new NonNullType(schema.stringType()));
|
|
44
45
|
joinGraph.addArgument('url', new NonNullType(schema.stringType()));
|
|
@@ -73,6 +74,8 @@ export class JoinSpecDefinition extends FeatureDefinition {
|
|
|
73
74
|
if (!this.isV01()) {
|
|
74
75
|
joinField.addArgument('type', schema.stringType());
|
|
75
76
|
joinField.addArgument('external', schema.booleanType());
|
|
77
|
+
joinField.addArgument('override', schema.stringType());
|
|
78
|
+
joinField.addArgument('usedOverridden', schema.booleanType());
|
|
76
79
|
}
|
|
77
80
|
|
|
78
81
|
if (!this.isV01()) {
|
|
@@ -88,6 +91,23 @@ export class JoinSpecDefinition extends FeatureDefinition {
|
|
|
88
91
|
const joinOwner = this.addDirective(schema, 'owner').addLocations(DirectiveLocation.OBJECT);
|
|
89
92
|
joinOwner.addArgument('graph', new NonNullType(graphEnum));
|
|
90
93
|
}
|
|
94
|
+
return [];
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
allElementNames(): string[] {
|
|
98
|
+
const names = [
|
|
99
|
+
'graph',
|
|
100
|
+
'Graph',
|
|
101
|
+
'FieldSet',
|
|
102
|
+
'@type',
|
|
103
|
+
'@field',
|
|
104
|
+
];
|
|
105
|
+
if (this.isV01()) {
|
|
106
|
+
names.push('@owner');
|
|
107
|
+
} else {
|
|
108
|
+
names.push('@implements');
|
|
109
|
+
}
|
|
110
|
+
return names;
|
|
91
111
|
}
|
|
92
112
|
|
|
93
113
|
populateGraphEnum(schema: Schema, subgraphs: Subgraphs): Map<string, string> {
|
|
@@ -141,7 +161,15 @@ export class JoinSpecDefinition extends FeatureDefinition {
|
|
|
141
161
|
return this.directive(schema, 'implements');
|
|
142
162
|
}
|
|
143
163
|
|
|
144
|
-
fieldDirective(schema: Schema): DirectiveDefinition<{
|
|
164
|
+
fieldDirective(schema: Schema): DirectiveDefinition<{
|
|
165
|
+
graph: string,
|
|
166
|
+
requires?: string,
|
|
167
|
+
provides?: string,
|
|
168
|
+
override?: string,
|
|
169
|
+
type?: string,
|
|
170
|
+
external?: boolean,
|
|
171
|
+
usedOverridden?: boolean,
|
|
172
|
+
}> {
|
|
145
173
|
return this.directive(schema, 'field')!;
|
|
146
174
|
}
|
|
147
175
|
|
|
@@ -160,3 +188,5 @@ export class JoinSpecDefinition extends FeatureDefinition {
|
|
|
160
188
|
export const JOIN_VERSIONS = new FeatureDefinitions<JoinSpecDefinition>(joinIdentity)
|
|
161
189
|
.add(new JoinSpecDefinition(new FeatureVersion(0, 1)))
|
|
162
190
|
.add(new JoinSpecDefinition(new FeatureVersion(0, 2)));
|
|
191
|
+
|
|
192
|
+
registerKnownFeature(JOIN_VERSIONS);
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { FeatureDefinition, FeatureDefinitions, FeatureUrl } from "./coreSpec";
|
|
2
|
+
|
|
3
|
+
const registeredFeatures: Map<string, FeatureDefinitions> = new Map();
|
|
4
|
+
|
|
5
|
+
export function registerKnownFeature(definitions: FeatureDefinitions) {
|
|
6
|
+
if (!registeredFeatures.has(definitions.identity)) {
|
|
7
|
+
registeredFeatures.set(definitions.identity, definitions);
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function coreFeatureDefinitionIfKnown(url: FeatureUrl): FeatureDefinition | undefined {
|
|
12
|
+
return registeredFeatures.get(url.identity)?.find(url.version);
|
|
13
|
+
}
|
package/src/operations.ts
CHANGED
|
@@ -862,6 +862,21 @@ export class SelectionSet {
|
|
|
862
862
|
}
|
|
863
863
|
}
|
|
864
864
|
|
|
865
|
+
export function allFieldDefinitionsInSelectionSet(selection: SelectionSet): FieldDefinition<CompositeType>[] {
|
|
866
|
+
const stack = Array.from(selection.selections());
|
|
867
|
+
const allFields: FieldDefinition<CompositeType>[] = [];
|
|
868
|
+
while (stack.length > 0) {
|
|
869
|
+
const selection = stack.pop()!;
|
|
870
|
+
if (selection.kind === 'FieldSelection') {
|
|
871
|
+
allFields.push(selection.field.definition);
|
|
872
|
+
}
|
|
873
|
+
if (selection.selectionSet) {
|
|
874
|
+
stack.push(...selection.selectionSet.selections());
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
return allFields;
|
|
878
|
+
}
|
|
879
|
+
|
|
865
880
|
export function selectionSetOfElement(element: OperationElement, subSelection?: SelectionSet): SelectionSet {
|
|
866
881
|
const selectionSet = new SelectionSet(element.parentType);
|
|
867
882
|
selectionSet.add(selectionOfElement(element, subSelection));
|
|
@@ -5,9 +5,7 @@ import {
|
|
|
5
5
|
federationMetadata,
|
|
6
6
|
FieldDefinition,
|
|
7
7
|
collectTargetFields,
|
|
8
|
-
|
|
9
|
-
ObjectType,
|
|
10
|
-
Schema
|
|
8
|
+
Schema,
|
|
11
9
|
} from ".";
|
|
12
10
|
|
|
13
11
|
export function computeShareables(schema: Schema): (field: FieldDefinition<CompositeType>) => boolean {
|
|
@@ -33,7 +31,7 @@ export function computeShareables(schema: Schema): (field: FieldDefinition<Compo
|
|
|
33
31
|
}
|
|
34
32
|
};
|
|
35
33
|
|
|
36
|
-
for (const type of schema.
|
|
34
|
+
for (const type of schema.objectTypes()) {
|
|
37
35
|
addKeyFields(type);
|
|
38
36
|
const shareablesOnType = shareableDirective ? type.appliedDirectivesOf(shareableDirective) : [];
|
|
39
37
|
for (const field of type.fields()) {
|
|
@@ -59,10 +57,9 @@ export function computeShareables(schema: Schema): (field: FieldDefinition<Compo
|
|
|
59
57
|
}
|
|
60
58
|
}
|
|
61
59
|
|
|
62
|
-
for (const type of schema.
|
|
60
|
+
for (const type of schema.interfaceTypes()) {
|
|
63
61
|
addKeyFields(type);
|
|
64
62
|
}
|
|
65
63
|
|
|
66
64
|
return (field) => shareableFields.has(field.coordinate);
|
|
67
65
|
}
|
|
68
|
-
|
package/src/print.ts
CHANGED
|
@@ -140,7 +140,7 @@ function printSchemaDefinitionOrExtension(
|
|
|
140
140
|
}
|
|
141
141
|
const rootEntries = orderRoots(roots, options).map((rootType) => `${options.indentString}${rootType.rootKind}: ${rootType.type}`);
|
|
142
142
|
// Note that that the description is never written with the extension as `extend schema` doesn _not_ support descriptions
|
|
143
|
-
return
|
|
143
|
+
return printDescription(schemaDefinition, options, extension)
|
|
144
144
|
+ printIsExtension(extension)
|
|
145
145
|
+ 'schema'
|
|
146
146
|
+ printAppliedDirectives(directives, options, true, rootEntries.length !== 0)
|
|
@@ -187,7 +187,7 @@ export function printTypeDefinitionAndExtensions(type: NamedType, options: Print
|
|
|
187
187
|
|
|
188
188
|
export function printDirectiveDefinition(directive: DirectiveDefinition, options: PrintOptions): string {
|
|
189
189
|
const locations = directive.locations.join(' | ');
|
|
190
|
-
return `${printDescription(directive, options)}directive ${directive}${printArgs(directive.arguments(), options)}${directive.repeatable ? ' repeatable' : ''} on ${locations}`;
|
|
190
|
+
return `${printDescription(directive, options, null)}directive ${directive}${printArgs(directive.arguments(), options)}${directive.repeatable ? ' repeatable' : ''} on ${locations}`;
|
|
191
191
|
}
|
|
192
192
|
|
|
193
193
|
function printAppliedDirectives(
|
|
@@ -207,10 +207,12 @@ function printAppliedDirectives(
|
|
|
207
207
|
function printDescription(
|
|
208
208
|
element: SchemaElement<any, any>,
|
|
209
209
|
options: PrintOptions,
|
|
210
|
+
extension: Extension<any> | undefined | null,
|
|
210
211
|
indentation: string = '',
|
|
211
212
|
firstInBlock: boolean = true
|
|
212
213
|
): string {
|
|
213
|
-
|
|
214
|
+
// Note that extensions cannot have descriptions (it's not syntactically valid)
|
|
215
|
+
if (extension || element.description === undefined || options.noDescriptions) {
|
|
214
216
|
return '';
|
|
215
217
|
}
|
|
216
218
|
|
|
@@ -227,7 +229,7 @@ function printScalarDefinitionOrExtension(type: ScalarType, options: PrintOption
|
|
|
227
229
|
if (extension && !directives.length) {
|
|
228
230
|
return undefined;
|
|
229
231
|
}
|
|
230
|
-
return `${printDescription(type, options)}${printIsExtension(extension)}scalar ${type.name}${printAppliedDirectives(directives, options, true, false)}`
|
|
232
|
+
return `${printDescription(type, options, extension)}${printIsExtension(extension)}scalar ${type.name}${printAppliedDirectives(directives, options, true, false)}`
|
|
231
233
|
}
|
|
232
234
|
|
|
233
235
|
function printImplementedInterfaces(implementations: readonly InterfaceImplementation<any>[]): string {
|
|
@@ -246,7 +248,7 @@ function printFieldBasedTypeDefinitionOrExtension(kind: string, type: ObjectType
|
|
|
246
248
|
if (!directives.length && !interfaces.length && !fields.length) {
|
|
247
249
|
return undefined;
|
|
248
250
|
}
|
|
249
|
-
return printDescription(type, options)
|
|
251
|
+
return printDescription(type, options, extension)
|
|
250
252
|
+ printIsExtension(extension)
|
|
251
253
|
+ kind + ' ' + type
|
|
252
254
|
+ printImplementedInterfaces(interfaces)
|
|
@@ -262,7 +264,7 @@ function printUnionDefinitionOrExtension(type: UnionType, options: PrintOptions,
|
|
|
262
264
|
return undefined;
|
|
263
265
|
}
|
|
264
266
|
const possibleTypes = members.length ? ' = ' + members.map(m => m.type).join(' | ') : '';
|
|
265
|
-
return printDescription(type, options)
|
|
267
|
+
return printDescription(type, options, extension)
|
|
266
268
|
+ printIsExtension(extension)
|
|
267
269
|
+ 'union ' + type
|
|
268
270
|
+ printAppliedDirectives(directives, options, true, members.length > 0)
|
|
@@ -276,11 +278,11 @@ function printEnumDefinitionOrExtension(type: EnumType, options: PrintOptions, e
|
|
|
276
278
|
return undefined;
|
|
277
279
|
}
|
|
278
280
|
const vals = values.map((v, i) =>
|
|
279
|
-
printDescription(v, options, options.indentString, !i)
|
|
281
|
+
printDescription(v, options, extension, options.indentString, !i)
|
|
280
282
|
+ options.indentString
|
|
281
283
|
+ v
|
|
282
284
|
+ printAppliedDirectives(v.appliedDirectives, options));
|
|
283
|
-
return printDescription(type, options)
|
|
285
|
+
return printDescription(type, options, extension)
|
|
284
286
|
+ printIsExtension(extension)
|
|
285
287
|
+ 'enum ' + type
|
|
286
288
|
+ printAppliedDirectives(directives, options, true, vals.length > 0)
|
|
@@ -294,7 +296,7 @@ function printInputDefinitionOrExtension(type: InputObjectType, options: PrintOp
|
|
|
294
296
|
if (!directives.length && !fields.length) {
|
|
295
297
|
return undefined;
|
|
296
298
|
}
|
|
297
|
-
return printDescription(type, options)
|
|
299
|
+
return printDescription(type, options, extension)
|
|
298
300
|
+ printIsExtension(extension)
|
|
299
301
|
+ 'input ' + type
|
|
300
302
|
+ printAppliedDirectives(directives, options, true, fields.length > 0)
|
|
@@ -304,7 +306,7 @@ function printInputDefinitionOrExtension(type: InputObjectType, options: PrintOp
|
|
|
304
306
|
|
|
305
307
|
function printFields(fields: readonly (FieldDefinition<any> | InputFieldDefinition)[], options: PrintOptions): string {
|
|
306
308
|
return printBlock(fields.map((f, i) =>
|
|
307
|
-
printDescription(f, options, options.indentString, !i)
|
|
309
|
+
printDescription(f, options, undefined, options.indentString, !i)
|
|
308
310
|
+ options.indentString
|
|
309
311
|
+ printField(f, options)
|
|
310
312
|
+ printAppliedDirectives(f.appliedDirectives, options)));
|
|
@@ -332,7 +334,7 @@ function printArgs(args: readonly ArgumentDefinition<any>[], options: PrintOptio
|
|
|
332
334
|
}
|
|
333
335
|
|
|
334
336
|
const formattedArgs = args
|
|
335
|
-
.map((arg, i) => printDescription(arg, options, ' ' + indentation, !i) + ' ' + indentation + printArg(arg, options))
|
|
337
|
+
.map((arg, i) => printDescription(arg, options, null, ' ' + indentation, !i) + ' ' + indentation + printArg(arg, options))
|
|
336
338
|
.join('\n');
|
|
337
339
|
return `(\n${formattedArgs}\n${indentation})`;
|
|
338
340
|
}
|
package/src/schemaUpgrader.ts
CHANGED
|
@@ -11,7 +11,6 @@ import {
|
|
|
11
11
|
errorCauses,
|
|
12
12
|
Extension,
|
|
13
13
|
FieldDefinition,
|
|
14
|
-
InterfaceType,
|
|
15
14
|
isCompositeType,
|
|
16
15
|
isInterfaceType,
|
|
17
16
|
isObjectType,
|
|
@@ -24,7 +23,6 @@ import {
|
|
|
24
23
|
import {
|
|
25
24
|
addSubgraphToError,
|
|
26
25
|
collectTargetFields,
|
|
27
|
-
collectUsedExternalFieldsCoordinates,
|
|
28
26
|
federationMetadata,
|
|
29
27
|
FederationMetadata,
|
|
30
28
|
printSubgraphNames,
|
|
@@ -43,7 +41,7 @@ type UpgradeChanges = MultiMap<UpgradeChangeID, UpgradeChange>;
|
|
|
43
41
|
export type UpgradeSuccess = {
|
|
44
42
|
subgraphs: Subgraphs,
|
|
45
43
|
changes: Map<string, UpgradeChanges>,
|
|
46
|
-
errors?: never,
|
|
44
|
+
errors?: never,
|
|
47
45
|
}
|
|
48
46
|
|
|
49
47
|
export type UpgradeFailure = {
|
|
@@ -235,7 +233,7 @@ export function upgradeSubgraphsIfNecessary(inputs: Subgraphs): UpgradeResult {
|
|
|
235
233
|
* 2. do not have a definition for the same type in the same subgraph (this is a GraphQL extension otherwise).
|
|
236
234
|
*
|
|
237
235
|
* Not that type extensions in federation 1 generally have a @key but in really the code consider something a type extension even without
|
|
238
|
-
* it (which I'd argue is a unintended bug of fed1 since this leads to various problems) so we don't check for the presence of @key here.
|
|
236
|
+
* it (which I'd argue is a unintended bug of fed1 since this leads to various problems) so we don't check for the presence of @key here.
|
|
239
237
|
*/
|
|
240
238
|
function isFederationTypeExtension(type: NamedType): boolean {
|
|
241
239
|
const metadata = federationMetadata(type.schema());
|
|
@@ -435,7 +433,7 @@ class SchemaUpgrader {
|
|
|
435
433
|
}
|
|
436
434
|
|
|
437
435
|
private removeExternalOnInterface() {
|
|
438
|
-
for (const itf of this.schema.
|
|
436
|
+
for (const itf of this.schema.interfaceTypes()) {
|
|
439
437
|
for (const field of itf.fields()) {
|
|
440
438
|
const external = this.external(field);
|
|
441
439
|
if (external) {
|
|
@@ -522,7 +520,7 @@ class SchemaUpgrader {
|
|
|
522
520
|
continue;
|
|
523
521
|
}
|
|
524
522
|
assert(isCompositeType(typeInOther), () => `Type ${type} is of kind ${type.kind} in ${this.subgraph.name} but ${typeInOther.kind} in ${other.name}`);
|
|
525
|
-
const keysInOther = typeInOther.appliedDirectivesOf(other.metadata().keyDirective());
|
|
523
|
+
const keysInOther = typeInOther.appliedDirectivesOf(other.metadata().keyDirective());
|
|
526
524
|
if (keysInOther.length === 0) {
|
|
527
525
|
continue;
|
|
528
526
|
}
|
|
@@ -564,14 +562,12 @@ class SchemaUpgrader {
|
|
|
564
562
|
}
|
|
565
563
|
|
|
566
564
|
private removeUnusedExternals() {
|
|
567
|
-
const usedExternalFieldsCoordinates = collectUsedExternalFieldsCoordinates(this.metadata);
|
|
568
|
-
|
|
569
565
|
for (const type of this.schema.types()) {
|
|
570
566
|
if (!isObjectType(type) && !isInterfaceType(type)) {
|
|
571
567
|
continue;
|
|
572
568
|
}
|
|
573
569
|
for (const field of type.fields()) {
|
|
574
|
-
if (this.metadata.isFieldExternal(field) && !
|
|
570
|
+
if (this.metadata.isFieldExternal(field) && !this.metadata.isFieldUsed(field)) {
|
|
575
571
|
this.addChange(new UnusedExternalRemoval(field.coordinate));
|
|
576
572
|
field.remove();
|
|
577
573
|
}
|
|
@@ -593,7 +589,7 @@ class SchemaUpgrader {
|
|
|
593
589
|
}
|
|
594
590
|
|
|
595
591
|
private removeDirectivesOnInterface() {
|
|
596
|
-
for (const type of this.schema.
|
|
592
|
+
for (const type of this.schema.interfaceTypes()) {
|
|
597
593
|
for (const application of type.appliedDirectivesOf(this.metadata.keyDirective())) {
|
|
598
594
|
this.addChange(new KeyOnInterfaceRemoval(type.name));
|
|
599
595
|
application.remove();
|
|
@@ -610,7 +606,7 @@ class SchemaUpgrader {
|
|
|
610
606
|
}
|
|
611
607
|
|
|
612
608
|
private removeProvidesOnNonComposite() {
|
|
613
|
-
for (const type of this.schema.
|
|
609
|
+
for (const type of this.schema.objectTypes()) {
|
|
614
610
|
for (const field of type.fields()) {
|
|
615
611
|
if (isCompositeType(baseType(field.type!))) {
|
|
616
612
|
continue;
|
|
@@ -630,7 +626,7 @@ class SchemaUpgrader {
|
|
|
630
626
|
// We add shareable:
|
|
631
627
|
// - to every "value type" (in the fed1 sense of non-root type and non-entity) if it is used in any other subgraphs
|
|
632
628
|
// - to any (non-external) field of an entity/root-type that is not a key field and if another subgraphs resolve it (fully or partially through @provides)
|
|
633
|
-
for (const type of this.schema.
|
|
629
|
+
for (const type of this.schema.objectTypes()) {
|
|
634
630
|
if (type.hasAppliedDirective(keyDirective) || type.isRootType()) {
|
|
635
631
|
for (const field of type.fields()) {
|
|
636
632
|
// To know if the field is a "key" field which doesn't need shareable, we rely on whether the field is shareable in the original
|
package/src/suggestions.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { mapKeys } from './utils';
|
|
|
5
5
|
* Given an invalid input string and a list of valid options, returns a filtered
|
|
6
6
|
* list of valid options sorted based on their similarity with the input.
|
|
7
7
|
*/
|
|
8
|
-
export function suggestionList(input: string, options: string[]): string[] {
|
|
8
|
+
export function suggestionList(input: string, options: readonly string[]): string[] {
|
|
9
9
|
const optionsByDistance = new Map<string, number>();
|
|
10
10
|
|
|
11
11
|
const threshold = Math.floor(input.length * 0.4) + 1;
|
package/src/supergraphs.ts
CHANGED
package/src/tagSpec.ts
CHANGED
|
@@ -1,28 +1,55 @@
|
|
|
1
1
|
import { DirectiveLocation, GraphQLError } from "graphql";
|
|
2
2
|
import { FeatureDefinition, FeatureDefinitions, FeatureUrl, FeatureVersion } from "./coreSpec";
|
|
3
3
|
import { DirectiveDefinition, NonNullType, Schema } from "./definitions";
|
|
4
|
+
import { createDirectiveSpecification, DirectiveSpecification } from "./directiveAndTypeSpecification";
|
|
4
5
|
import { ERRORS } from "./error";
|
|
6
|
+
import { registerKnownFeature } from "./knownCoreFeatures";
|
|
5
7
|
import { sameType } from "./types";
|
|
6
8
|
|
|
7
9
|
export const tagIdentity = 'https://specs.apollo.dev/tag';
|
|
8
10
|
|
|
9
|
-
export const tagLocations = [
|
|
10
|
-
DirectiveLocation.FIELD_DEFINITION,
|
|
11
|
-
DirectiveLocation.OBJECT,
|
|
12
|
-
DirectiveLocation.INTERFACE,
|
|
13
|
-
DirectiveLocation.UNION,
|
|
14
|
-
];
|
|
15
|
-
|
|
16
|
-
const printedTagDefinition = 'directive @tag(name: String!) repeatable on FIELD_DEFINITION | INTERFACE | OBJECT | UNION';
|
|
17
|
-
|
|
18
11
|
export class TagSpecDefinition extends FeatureDefinition {
|
|
12
|
+
public readonly tagLocations: DirectiveLocation[];
|
|
13
|
+
public readonly tagDirectiveSpec: DirectiveSpecification;
|
|
14
|
+
private readonly printedTagDefinition: string;
|
|
15
|
+
|
|
19
16
|
constructor(version: FeatureVersion) {
|
|
20
17
|
super(new FeatureUrl(tagIdentity, 'tag', version));
|
|
18
|
+
this.tagLocations = [
|
|
19
|
+
DirectiveLocation.FIELD_DEFINITION,
|
|
20
|
+
DirectiveLocation.OBJECT,
|
|
21
|
+
DirectiveLocation.INTERFACE,
|
|
22
|
+
DirectiveLocation.UNION,
|
|
23
|
+
];
|
|
24
|
+
this.printedTagDefinition = 'directive @tag(name: String!) repeatable on FIELD_DEFINITION | INTERFACE | OBJECT | UNION';
|
|
25
|
+
if (!this.isV01()) {
|
|
26
|
+
this.tagLocations.push(
|
|
27
|
+
DirectiveLocation.ARGUMENT_DEFINITION,
|
|
28
|
+
DirectiveLocation.SCALAR,
|
|
29
|
+
DirectiveLocation.ENUM,
|
|
30
|
+
DirectiveLocation.ENUM_VALUE,
|
|
31
|
+
DirectiveLocation.INPUT_OBJECT,
|
|
32
|
+
DirectiveLocation.INPUT_FIELD_DEFINITION,
|
|
33
|
+
);
|
|
34
|
+
this.printedTagDefinition = 'directive @tag(name: String!) repeatable on FIELD_DEFINITION | INTERFACE | OBJECT | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION';
|
|
35
|
+
}
|
|
36
|
+
this.tagDirectiveSpec = createDirectiveSpecification({
|
|
37
|
+
name:'tag',
|
|
38
|
+
locations: this.tagLocations,
|
|
39
|
+
repeatable: true,
|
|
40
|
+
argumentFct: (schema) => ({
|
|
41
|
+
args: [{ name: 'name', type: new NonNullType(schema.stringType()) }],
|
|
42
|
+
errors: [],
|
|
43
|
+
}),
|
|
44
|
+
});
|
|
21
45
|
}
|
|
22
46
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
47
|
+
private isV01() {
|
|
48
|
+
return this.version.equals(new FeatureVersion(0, 1));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
addElementsToSchema(schema: Schema): GraphQLError[] {
|
|
52
|
+
return this.addDirectiveSpec(schema, this.tagDirectiveSpec);
|
|
26
53
|
}
|
|
27
54
|
|
|
28
55
|
tagDirective(schema: Schema): DirectiveDefinition<{name: string}> {
|
|
@@ -33,16 +60,23 @@ export class TagSpecDefinition extends FeatureDefinition {
|
|
|
33
60
|
const hasUnknownArguments = Object.keys(definition.arguments()).length > 1;
|
|
34
61
|
const nameArg = definition.argument('name');
|
|
35
62
|
const hasValidNameArg = nameArg && sameType(nameArg.type!, new NonNullType(definition.schema().stringType()));
|
|
36
|
-
const hasValidLocations = definition.locations.every(loc => tagLocations.includes(loc));
|
|
63
|
+
const hasValidLocations = definition.locations.every(loc => this.tagLocations.includes(loc));
|
|
37
64
|
if (hasUnknownArguments || !hasValidNameArg || !hasValidLocations) {
|
|
38
65
|
return ERRORS.DIRECTIVE_DEFINITION_INVALID.err({
|
|
39
|
-
message: `Found invalid @tag directive definition. Please ensure the directive definition in your schema's definitions matches the following:\n\t${printedTagDefinition}`,
|
|
66
|
+
message: `Found invalid @tag directive definition. Please ensure the directive definition in your schema's definitions matches the following:\n\t${this.printedTagDefinition}`,
|
|
40
67
|
}
|
|
41
68
|
);
|
|
42
69
|
}
|
|
43
70
|
return undefined;
|
|
44
71
|
}
|
|
72
|
+
|
|
73
|
+
allElementNames(): string[] {
|
|
74
|
+
return ["@tag"];
|
|
75
|
+
}
|
|
45
76
|
}
|
|
46
77
|
|
|
47
78
|
export const TAG_VERSIONS = new FeatureDefinitions<TagSpecDefinition>(tagIdentity)
|
|
48
|
-
.add(new TagSpecDefinition(new FeatureVersion(0, 1)))
|
|
79
|
+
.add(new TagSpecDefinition(new FeatureVersion(0, 1)))
|
|
80
|
+
.add(new TagSpecDefinition(new FeatureVersion(0, 2)));
|
|
81
|
+
|
|
82
|
+
registerKnownFeature(TAG_VERSIONS);
|