@apollo/federation-internals 2.8.0-connectors.5 → 2.8.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.
- package/dist/directiveAndTypeSpecification.d.ts +13 -1
- package/dist/directiveAndTypeSpecification.d.ts.map +1 -1
- package/dist/directiveAndTypeSpecification.js +2 -2
- package/dist/directiveAndTypeSpecification.js.map +1 -1
- package/dist/error.d.ts +25 -0
- package/dist/error.d.ts.map +1 -1
- package/dist/error.js +50 -0
- package/dist/error.js.map +1 -1
- package/dist/extractSubgraphsFromSupergraph.d.ts +1 -1
- package/dist/extractSubgraphsFromSupergraph.d.ts.map +1 -1
- package/dist/extractSubgraphsFromSupergraph.js +62 -7
- package/dist/extractSubgraphsFromSupergraph.js.map +1 -1
- package/dist/federation.d.ts +19 -2
- package/dist/federation.d.ts.map +1 -1
- package/dist/federation.js +420 -7
- package/dist/federation.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/knownCoreFeatures.d.ts +3 -0
- package/dist/knownCoreFeatures.d.ts.map +1 -1
- package/dist/knownCoreFeatures.js +12 -1
- package/dist/knownCoreFeatures.js.map +1 -1
- package/dist/operations.d.ts +10 -8
- package/dist/operations.d.ts.map +1 -1
- package/dist/operations.js +48 -16
- package/dist/operations.js.map +1 -1
- package/dist/specs/contextSpec.d.ts +20 -0
- package/dist/specs/contextSpec.d.ts.map +1 -0
- package/dist/specs/contextSpec.js +62 -0
- package/dist/specs/contextSpec.js.map +1 -0
- package/dist/specs/coreSpec.d.ts +1 -0
- package/dist/specs/coreSpec.d.ts.map +1 -1
- package/dist/specs/coreSpec.js +3 -0
- package/dist/specs/coreSpec.js.map +1 -1
- package/dist/specs/federationSpec.d.ts +8 -2
- package/dist/specs/federationSpec.d.ts.map +1 -1
- package/dist/specs/federationSpec.js +16 -1
- package/dist/specs/federationSpec.js.map +1 -1
- package/dist/specs/joinSpec.d.ts +6 -0
- package/dist/specs/joinSpec.d.ts.map +1 -1
- package/dist/specs/joinSpec.js +11 -1
- package/dist/specs/joinSpec.js.map +1 -1
- package/dist/specs/sourceSpec.d.ts +69 -0
- package/dist/specs/sourceSpec.d.ts.map +1 -0
- package/dist/specs/sourceSpec.js +345 -0
- package/dist/specs/sourceSpec.js.map +1 -0
- package/dist/supergraphs.d.ts +4 -0
- package/dist/supergraphs.d.ts.map +1 -1
- package/dist/supergraphs.js +35 -2
- package/dist/supergraphs.js.map +1 -1
- package/dist/utils.d.ts +3 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +39 -1
- package/dist/utils.js.map +1 -1
- package/package.json +1 -1
- package/src/directiveAndTypeSpecification.ts +8 -1
- package/src/error.ts +175 -0
- package/src/extractSubgraphsFromSupergraph.ts +76 -14
- package/src/federation.ts +636 -15
- package/src/index.ts +2 -1
- package/src/knownCoreFeatures.ts +15 -0
- package/src/operations.ts +63 -23
- package/src/specs/contextSpec.ts +87 -0
- package/src/specs/coreSpec.ts +5 -0
- package/src/specs/federationSpec.ts +18 -1
- package/src/specs/joinSpec.ts +27 -3
- package/src/specs/sourceSpec.ts +607 -0
- package/src/supergraphs.ts +37 -1
- package/src/utils.ts +38 -0
- package/dist/specs/connectSpec.d.ts +0 -42
- package/dist/specs/connectSpec.d.ts.map +0 -1
- package/dist/specs/connectSpec.js +0 -82
- package/dist/specs/connectSpec.js.map +0 -1
- package/src/specs/connectSpec.ts +0 -181
package/src/index.ts
CHANGED
|
@@ -12,6 +12,7 @@ export * from './specs/joinSpec';
|
|
|
12
12
|
export * from './specs/tagSpec';
|
|
13
13
|
export * from './specs/inaccessibleSpec';
|
|
14
14
|
export * from './specs/federationSpec';
|
|
15
|
+
export * from './specs/contextSpec';
|
|
15
16
|
export * from './supergraphs';
|
|
16
17
|
export * from './error';
|
|
17
18
|
export * from './schemaUpgrader';
|
|
@@ -23,4 +24,4 @@ export * from './argumentCompositionStrategies';
|
|
|
23
24
|
export * from './specs/authenticatedSpec';
|
|
24
25
|
export * from './specs/requiresScopesSpec';
|
|
25
26
|
export * from './specs/policySpec';
|
|
26
|
-
export * from './specs/
|
|
27
|
+
export * from './specs/sourceSpec';
|
package/src/knownCoreFeatures.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { GraphQLError } from "graphql";
|
|
2
|
+
import { Schema } from "./definitions";
|
|
1
3
|
import { FeatureDefinition, FeatureDefinitions, FeatureUrl } from "./specs/coreSpec";
|
|
2
4
|
|
|
3
5
|
const registeredFeatures = new Map<string, FeatureDefinitions>();
|
|
@@ -12,6 +14,19 @@ export function coreFeatureDefinitionIfKnown(url: FeatureUrl): FeatureDefinition
|
|
|
12
14
|
return registeredFeatures.get(url.identity)?.find(url.version);
|
|
13
15
|
}
|
|
14
16
|
|
|
17
|
+
export function validateKnownFeatures(
|
|
18
|
+
schema: Schema,
|
|
19
|
+
errorCollector: GraphQLError[] = [],
|
|
20
|
+
): GraphQLError[] {
|
|
21
|
+
registeredFeatures.forEach(definitions => {
|
|
22
|
+
const feature = definitions.latest();
|
|
23
|
+
if (feature.validateSubgraphSchema !== FeatureDefinition.prototype.validateSubgraphSchema) {
|
|
24
|
+
errorCollector.push(...feature.validateSubgraphSchema(schema));
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
return errorCollector;
|
|
28
|
+
}
|
|
29
|
+
|
|
15
30
|
/**
|
|
16
31
|
* Removes a feature from the set of known features.
|
|
17
32
|
*
|
package/src/operations.ts
CHANGED
|
@@ -21,11 +21,9 @@ import {
|
|
|
21
21
|
Directive,
|
|
22
22
|
DirectiveTargetElement,
|
|
23
23
|
FieldDefinition,
|
|
24
|
-
InterfaceType,
|
|
25
24
|
isCompositeType,
|
|
26
25
|
isInterfaceType,
|
|
27
26
|
isNullableType,
|
|
28
|
-
ObjectType,
|
|
29
27
|
runtimeTypesIntersects,
|
|
30
28
|
Schema,
|
|
31
29
|
SchemaRootKind,
|
|
@@ -51,7 +49,7 @@ import {
|
|
|
51
49
|
directivesToString,
|
|
52
50
|
directivesToDirectiveNodes,
|
|
53
51
|
} from "./definitions";
|
|
54
|
-
import { isInterfaceObjectType } from "./federation";
|
|
52
|
+
import { federationMetadata, isFederationDirectiveDefinedInSchema, isInterfaceObjectType } from "./federation";
|
|
55
53
|
import { ERRORS } from "./error";
|
|
56
54
|
import { isSubtype, sameType, typesCanBeMerged } from "./types";
|
|
57
55
|
import { assert, mapKeys, mapValues, MapWithCachedArrays, MultiMap, SetMultiMap } from "./utils";
|
|
@@ -170,6 +168,17 @@ export class Field<TArgs extends {[key: string]: any} = {[key: string]: any}> ex
|
|
|
170
168
|
baseType(): NamedType {
|
|
171
169
|
return baseType(this.definition.type!);
|
|
172
170
|
}
|
|
171
|
+
|
|
172
|
+
withUpdatedArguments(newArgs: TArgs): Field<TArgs> {
|
|
173
|
+
const newField = new Field<TArgs>(
|
|
174
|
+
this.definition,
|
|
175
|
+
{ ...this.args, ...newArgs },
|
|
176
|
+
this.appliedDirectives,
|
|
177
|
+
this.alias,
|
|
178
|
+
);
|
|
179
|
+
this.copyAttachementsTo(newField);
|
|
180
|
+
return newField;
|
|
181
|
+
}
|
|
173
182
|
|
|
174
183
|
withUpdatedDefinition(newDefinition: FieldDefinition<any>): Field<TArgs> {
|
|
175
184
|
const newField = new Field<TArgs>(
|
|
@@ -222,17 +231,12 @@ export class Field<TArgs extends {[key: string]: any} = {[key: string]: any}> ex
|
|
|
222
231
|
};
|
|
223
232
|
});
|
|
224
233
|
}
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
appliesTo(type: ObjectType | InterfaceType): boolean {
|
|
228
|
-
const definition = type.field(this.name);
|
|
229
|
-
return !!definition && this.selects(definition);
|
|
230
|
-
}
|
|
231
|
-
|
|
234
|
+
|
|
232
235
|
selects(
|
|
233
236
|
definition: FieldDefinition<any>,
|
|
234
237
|
assumeValid: boolean = false,
|
|
235
238
|
variableDefinitions?: VariableDefinitions,
|
|
239
|
+
contextualArguments?: string[],
|
|
236
240
|
): boolean {
|
|
237
241
|
assert(assumeValid || variableDefinitions, 'Must provide variable definitions if validation is needed');
|
|
238
242
|
|
|
@@ -252,7 +256,7 @@ export class Field<TArgs extends {[key: string]: any} = {[key: string]: any}> ex
|
|
|
252
256
|
for (const argDef of definition.arguments()) {
|
|
253
257
|
const appliedValue = this.argumentValue(argDef.name);
|
|
254
258
|
if (appliedValue === undefined) {
|
|
255
|
-
if (argDef.defaultValue === undefined && !isNullableType(argDef.type!)) {
|
|
259
|
+
if (argDef.defaultValue === undefined && !isNullableType(argDef.type!) && (!contextualArguments || !contextualArguments?.includes(argDef.name))) {
|
|
256
260
|
return false;
|
|
257
261
|
}
|
|
258
262
|
} else {
|
|
@@ -273,19 +277,28 @@ export class Field<TArgs extends {[key: string]: any} = {[key: string]: any}> ex
|
|
|
273
277
|
return true;
|
|
274
278
|
}
|
|
275
279
|
|
|
276
|
-
validate(variableDefinitions: VariableDefinitions) {
|
|
280
|
+
validate(variableDefinitions: VariableDefinitions, validateContextualArgs: boolean) {
|
|
277
281
|
validate(this.name === this.definition.name, () => `Field name "${this.name}" cannot select field "${this.definition.coordinate}: name mismatch"`);
|
|
278
|
-
|
|
282
|
+
|
|
283
|
+
|
|
279
284
|
// We need to make sure the field has valid values for every non-optional argument.
|
|
280
285
|
for (const argDef of this.definition.arguments()) {
|
|
281
286
|
const appliedValue = this.argumentValue(argDef.name);
|
|
287
|
+
|
|
288
|
+
let isContextualArg = false;
|
|
289
|
+
const schema = this.definition.schema();
|
|
290
|
+
const fromContextDirective = federationMetadata(schema)?.fromContextDirective();
|
|
291
|
+
if (fromContextDirective && isFederationDirectiveDefinedInSchema(fromContextDirective)) {
|
|
292
|
+
isContextualArg = argDef.appliedDirectivesOf(fromContextDirective).length > 0;
|
|
293
|
+
}
|
|
294
|
+
|
|
282
295
|
if (appliedValue === undefined) {
|
|
283
296
|
validate(
|
|
284
|
-
argDef.defaultValue !== undefined || isNullableType(argDef.type!),
|
|
297
|
+
(isContextualArg && !validateContextualArgs) || argDef.defaultValue !== undefined || isNullableType(argDef.type!),
|
|
285
298
|
() => `Missing mandatory value for argument "${argDef.name}" of field "${this.definition.coordinate}" in selection "${this}"`);
|
|
286
299
|
} else {
|
|
287
300
|
validate(
|
|
288
|
-
isValidValue(appliedValue, argDef, variableDefinitions),
|
|
301
|
+
(isContextualArg && !validateContextualArgs) || isValidValue(appliedValue, argDef, variableDefinitions),
|
|
289
302
|
() => `Invalid value ${valueToString(appliedValue)} for argument "${argDef.coordinate}" of type ${argDef.type}`)
|
|
290
303
|
}
|
|
291
304
|
}
|
|
@@ -355,10 +368,23 @@ export class Field<TArgs extends {[key: string]: any} = {[key: string]: any}> ex
|
|
|
355
368
|
if (this.name === typenameFieldName) {
|
|
356
369
|
return parentType.typenameField()?.type;
|
|
357
370
|
}
|
|
358
|
-
|
|
359
|
-
|
|
371
|
+
|
|
372
|
+
const returnType = this.canRebaseOn(parentType)
|
|
360
373
|
? parentType.field(this.name)?.type
|
|
361
374
|
: undefined;
|
|
375
|
+
|
|
376
|
+
// If the field has an argument with fromContextDirective on it. We should not rebase it.
|
|
377
|
+
const fromContextDirective = federationMetadata(parentType.schema())?.fromContextDirective();
|
|
378
|
+
if (fromContextDirective && isFederationDirectiveDefinedInSchema(fromContextDirective)) {
|
|
379
|
+
const fieldInParent = parentType.field(this.name);
|
|
380
|
+
if (fieldInParent && fieldInParent.arguments()
|
|
381
|
+
.some(arg => arg.appliedDirectivesOf(fromContextDirective).length > 0 && (!this.args || this.args[arg.name] === undefined))
|
|
382
|
+
) {
|
|
383
|
+
return undefined;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
return returnType;
|
|
362
388
|
}
|
|
363
389
|
|
|
364
390
|
hasDefer(): boolean {
|
|
@@ -1998,10 +2024,10 @@ export class SelectionSet {
|
|
|
1998
2024
|
return this.selections().every((selection) => selection.canAddTo(parentTypeToTest));
|
|
1999
2025
|
}
|
|
2000
2026
|
|
|
2001
|
-
validate(variableDefinitions: VariableDefinitions) {
|
|
2027
|
+
validate(variableDefinitions: VariableDefinitions, validateContextualArgs: boolean = false) {
|
|
2002
2028
|
validate(!this.isEmpty(), () => `Invalid empty selection set`);
|
|
2003
2029
|
for (const selection of this.selections()) {
|
|
2004
|
-
selection.validate(variableDefinitions);
|
|
2030
|
+
selection.validate(variableDefinitions, validateContextualArgs);
|
|
2005
2031
|
}
|
|
2006
2032
|
}
|
|
2007
2033
|
|
|
@@ -2520,7 +2546,7 @@ abstract class AbstractSelection<TElement extends OperationElement, TIsLeaf exte
|
|
|
2520
2546
|
|
|
2521
2547
|
abstract toSelectionNode(): SelectionNode;
|
|
2522
2548
|
|
|
2523
|
-
abstract validate(variableDefinitions: VariableDefinitions): void;
|
|
2549
|
+
abstract validate(variableDefinitions: VariableDefinitions, validateContextualArgs: boolean): void;
|
|
2524
2550
|
|
|
2525
2551
|
abstract rebaseOn(args: { parentType: CompositeType, fragments: NamedFragments | undefined, errorIfCannotRebase: boolean}): TOwnType | undefined;
|
|
2526
2552
|
|
|
@@ -3035,8 +3061,8 @@ export class FieldSelection extends AbstractSelection<Field<any>, undefined, Fie
|
|
|
3035
3061
|
return predicate(thisWithFilteredSelectionSet) ? thisWithFilteredSelectionSet : undefined;
|
|
3036
3062
|
}
|
|
3037
3063
|
|
|
3038
|
-
validate(variableDefinitions: VariableDefinitions) {
|
|
3039
|
-
this.element.validate(variableDefinitions);
|
|
3064
|
+
validate(variableDefinitions: VariableDefinitions, validateContextualArgs: boolean) {
|
|
3065
|
+
this.element.validate(variableDefinitions, validateContextualArgs);
|
|
3040
3066
|
// Note that validation is kind of redundant since `this.selectionSet.validate()` will check that it isn't empty. But doing it
|
|
3041
3067
|
// allow to provide much better error messages.
|
|
3042
3068
|
validate(
|
|
@@ -3955,7 +3981,7 @@ export function parseSelectionSet({
|
|
|
3955
3981
|
return selectionSet;
|
|
3956
3982
|
}
|
|
3957
3983
|
|
|
3958
|
-
function parseOperationAST(source: string): OperationDefinitionNode {
|
|
3984
|
+
export function parseOperationAST(source: string): OperationDefinitionNode {
|
|
3959
3985
|
const parsed = parse(source);
|
|
3960
3986
|
validate(parsed.definitions.length === 1, () => 'Selections should contain a single definitions, found ' + parsed.definitions.length);
|
|
3961
3987
|
const def = parsed.definitions[0];
|
|
@@ -3980,3 +4006,17 @@ export function operationToDocument(operation: Operation): DocumentNode {
|
|
|
3980
4006
|
definitions: [operationAST as DefinitionNode].concat(fragmentASTs),
|
|
3981
4007
|
};
|
|
3982
4008
|
}
|
|
4009
|
+
|
|
4010
|
+
export function hasSelectionWithPredicate(selectionSet: SelectionSet, predicate: (s: Selection) => boolean): boolean {
|
|
4011
|
+
for (const selection of selectionSet.selections()) {
|
|
4012
|
+
if (predicate(selection)) {
|
|
4013
|
+
return true;
|
|
4014
|
+
}
|
|
4015
|
+
if (selection.selectionSet) {
|
|
4016
|
+
if (hasSelectionWithPredicate(selection.selectionSet, predicate)) {
|
|
4017
|
+
return true;
|
|
4018
|
+
}
|
|
4019
|
+
}
|
|
4020
|
+
}
|
|
4021
|
+
return false;
|
|
4022
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { DirectiveLocation } from "graphql";
|
|
2
|
+
import {
|
|
3
|
+
CorePurpose,
|
|
4
|
+
FeatureDefinition,
|
|
5
|
+
FeatureDefinitions,
|
|
6
|
+
FeatureUrl,
|
|
7
|
+
FeatureVersion,
|
|
8
|
+
} from "./coreSpec";
|
|
9
|
+
import { DirectiveDefinition, NonNullType, Schema, isInputType } from "../definitions";
|
|
10
|
+
import { DirectiveSpecification, createDirectiveSpecification, createScalarTypeSpecification } from "../directiveAndTypeSpecification";
|
|
11
|
+
import { registerKnownFeature } from "../knownCoreFeatures";
|
|
12
|
+
import { Subgraph } from '../federation';
|
|
13
|
+
import { assert } from '../utils';
|
|
14
|
+
|
|
15
|
+
export enum ContextDirectiveName {
|
|
16
|
+
CONTEXT = 'context',
|
|
17
|
+
FROM_CONTEXT = 'fromContext',
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const fieldValueScalar = 'ContextFieldValue';
|
|
21
|
+
|
|
22
|
+
export class ContextSpecDefinition extends FeatureDefinition {
|
|
23
|
+
public static readonly directiveName = 'context';
|
|
24
|
+
public static readonly identity =
|
|
25
|
+
`https://specs.apollo.dev/${ContextSpecDefinition.directiveName}`;
|
|
26
|
+
public readonly contextDirectiveSpec: DirectiveSpecification;
|
|
27
|
+
public readonly fromContextDirectiveSpec: DirectiveSpecification;
|
|
28
|
+
|
|
29
|
+
constructor(version: FeatureVersion) {
|
|
30
|
+
super(
|
|
31
|
+
new FeatureUrl(
|
|
32
|
+
ContextSpecDefinition.identity,
|
|
33
|
+
ContextSpecDefinition.directiveName,
|
|
34
|
+
version,
|
|
35
|
+
)
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
this.registerType(createScalarTypeSpecification({ name: fieldValueScalar }));
|
|
39
|
+
|
|
40
|
+
this.contextDirectiveSpec = createDirectiveSpecification({
|
|
41
|
+
name: ContextDirectiveName.CONTEXT,
|
|
42
|
+
locations: [DirectiveLocation.INTERFACE, DirectiveLocation.OBJECT, DirectiveLocation.UNION],
|
|
43
|
+
args: [{ name: 'name', type: (schema) => new NonNullType(schema.stringType())}],
|
|
44
|
+
composes: true,
|
|
45
|
+
repeatable: true,
|
|
46
|
+
supergraphSpecification: (fedVersion) => CONTEXT_VERSIONS.getMinimumRequiredVersion(fedVersion),
|
|
47
|
+
staticArgumentTransform: (subgraph: Subgraph, args: {[key: string]: any}) => {
|
|
48
|
+
const subgraphName = subgraph.name;
|
|
49
|
+
return {
|
|
50
|
+
name: `${subgraphName}__${args.name}`,
|
|
51
|
+
};
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
this.fromContextDirectiveSpec = createDirectiveSpecification({
|
|
56
|
+
name: ContextDirectiveName.FROM_CONTEXT,
|
|
57
|
+
locations: [DirectiveLocation.ARGUMENT_DEFINITION],
|
|
58
|
+
args: [{ name: 'field', type: (schema, feature) => {
|
|
59
|
+
assert(feature, "Shouldn't be added without being attached to a @link spec");
|
|
60
|
+
const fieldValue = feature.typeNameInSchema(fieldValueScalar);
|
|
61
|
+
const fieldValueType = schema.type(fieldValue);
|
|
62
|
+
assert(fieldValueType, () => `Expected "${fieldValue}" to be defined`);
|
|
63
|
+
assert(isInputType(fieldValueType), `Expected "${fieldValue}" to be an input type`);
|
|
64
|
+
return fieldValueType;
|
|
65
|
+
}}],
|
|
66
|
+
composes: false,
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
this.registerDirective(this.contextDirectiveSpec);
|
|
70
|
+
this.registerDirective(this.fromContextDirectiveSpec);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
get defaultCorePurpose(): CorePurpose {
|
|
74
|
+
return 'SECURITY';
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
contextDirective(schema: Schema): DirectiveDefinition<{ name: string }> | undefined {
|
|
78
|
+
return this.directive(schema, ContextSpecDefinition.directiveName);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export const CONTEXT_VERSIONS =
|
|
83
|
+
new FeatureDefinitions<ContextSpecDefinition>(
|
|
84
|
+
ContextSpecDefinition.identity
|
|
85
|
+
).add(new ContextSpecDefinition(new FeatureVersion(0, 1)));
|
|
86
|
+
|
|
87
|
+
registerKnownFeature(CONTEXT_VERSIONS);
|
package/src/specs/coreSpec.ts
CHANGED
|
@@ -117,6 +117,11 @@ export abstract class FeatureDefinition {
|
|
|
117
117
|
.concat(this.typeSpecs().map((spec) => spec.name));
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
+
// No-op implementation that can be overridden by subclasses.
|
|
121
|
+
validateSubgraphSchema(_schema: Schema): GraphQLError[] {
|
|
122
|
+
return [];
|
|
123
|
+
}
|
|
124
|
+
|
|
120
125
|
protected nameInSchema(schema: Schema): string | undefined {
|
|
121
126
|
const feature = this.featureInSchema(schema);
|
|
122
127
|
return feature?.nameInSchema;
|
|
@@ -18,11 +18,14 @@ import { INACCESSIBLE_VERSIONS } from "./inaccessibleSpec";
|
|
|
18
18
|
import { AUTHENTICATED_VERSIONS } from "./authenticatedSpec";
|
|
19
19
|
import { REQUIRES_SCOPES_VERSIONS } from "./requiresScopesSpec";
|
|
20
20
|
import { POLICY_VERSIONS } from './policySpec';
|
|
21
|
+
import { SOURCE_VERSIONS } from './sourceSpec';
|
|
22
|
+
import { CONTEXT_VERSIONS } from './contextSpec';
|
|
21
23
|
|
|
22
24
|
export const federationIdentity = 'https://specs.apollo.dev/federation';
|
|
23
25
|
|
|
24
26
|
export enum FederationTypeName {
|
|
25
27
|
FIELD_SET = 'FieldSet',
|
|
28
|
+
CONTEXT_FIELD_VALUE = 'ContextFieldValue',
|
|
26
29
|
}
|
|
27
30
|
|
|
28
31
|
export enum FederationDirectiveName {
|
|
@@ -40,6 +43,11 @@ export enum FederationDirectiveName {
|
|
|
40
43
|
AUTHENTICATED = 'authenticated',
|
|
41
44
|
REQUIRES_SCOPES = 'requiresScopes',
|
|
42
45
|
POLICY = 'policy',
|
|
46
|
+
SOURCE_API = 'sourceAPI',
|
|
47
|
+
SOURCE_TYPE = 'sourceType',
|
|
48
|
+
SOURCE_FIELD = 'sourceField',
|
|
49
|
+
CONTEXT = 'context',
|
|
50
|
+
FROM_CONTEXT = 'fromContext',
|
|
43
51
|
}
|
|
44
52
|
|
|
45
53
|
const fieldSetTypeSpec = createScalarTypeSpecification({ name: FederationTypeName.FIELD_SET });
|
|
@@ -166,6 +174,14 @@ export class FederationSpecDefinition extends FeatureDefinition {
|
|
|
166
174
|
if (version.gte(new FeatureVersion(2, 6))) {
|
|
167
175
|
this.registerSubFeature(POLICY_VERSIONS.find(new FeatureVersion(0, 1))!);
|
|
168
176
|
}
|
|
177
|
+
|
|
178
|
+
if (version.gte(new FeatureVersion(2, 7))) {
|
|
179
|
+
this.registerSubFeature(SOURCE_VERSIONS.find(new FeatureVersion(0, 1))!);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (version.gte(new FeatureVersion(2, 8))) {
|
|
183
|
+
this.registerSubFeature(CONTEXT_VERSIONS.find(new FeatureVersion(0, 1))!);
|
|
184
|
+
}
|
|
169
185
|
}
|
|
170
186
|
}
|
|
171
187
|
|
|
@@ -177,6 +193,7 @@ export const FEDERATION_VERSIONS = new FeatureDefinitions<FederationSpecDefiniti
|
|
|
177
193
|
.add(new FederationSpecDefinition(new FeatureVersion(2, 4)))
|
|
178
194
|
.add(new FederationSpecDefinition(new FeatureVersion(2, 5)))
|
|
179
195
|
.add(new FederationSpecDefinition(new FeatureVersion(2, 6)))
|
|
180
|
-
.add(new FederationSpecDefinition(new FeatureVersion(2, 7)))
|
|
196
|
+
.add(new FederationSpecDefinition(new FeatureVersion(2, 7)))
|
|
197
|
+
.add(new FederationSpecDefinition(new FeatureVersion(2, 8)));
|
|
181
198
|
|
|
182
199
|
registerKnownFeature(FEDERATION_VERSIONS);
|
package/src/specs/joinSpec.ts
CHANGED
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
Schema,
|
|
8
8
|
NonNullType,
|
|
9
9
|
ListType,
|
|
10
|
+
InputObjectType,
|
|
10
11
|
} from "../definitions";
|
|
11
12
|
import { Subgraph, Subgraphs } from "../federation";
|
|
12
13
|
import { registerKnownFeature } from '../knownCoreFeatures';
|
|
@@ -36,7 +37,7 @@ export type JoinTypeDirectiveArguments = {
|
|
|
36
37
|
key?: string,
|
|
37
38
|
extension?: boolean,
|
|
38
39
|
resolvable?: boolean,
|
|
39
|
-
isInterfaceObject?: boolean
|
|
40
|
+
isInterfaceObject?: boolean,
|
|
40
41
|
};
|
|
41
42
|
|
|
42
43
|
export type JoinFieldDirectiveArguments = {
|
|
@@ -48,6 +49,12 @@ export type JoinFieldDirectiveArguments = {
|
|
|
48
49
|
external?: boolean,
|
|
49
50
|
usedOverridden?: boolean,
|
|
50
51
|
overrideLabel?: string,
|
|
52
|
+
contextArguments?: {
|
|
53
|
+
name: string,
|
|
54
|
+
type: string,
|
|
55
|
+
context: string,
|
|
56
|
+
selection: string,
|
|
57
|
+
}[],
|
|
51
58
|
}
|
|
52
59
|
|
|
53
60
|
export type JoinDirectiveArguments = {
|
|
@@ -151,9 +158,24 @@ export class JoinSpecDefinition extends FeatureDefinition {
|
|
|
151
158
|
joinDirective.addArgument('name', new NonNullType(schema.stringType()));
|
|
152
159
|
joinDirective.addArgument('args', this.addScalarType(schema, 'DirectiveArguments'));
|
|
153
160
|
|
|
154
|
-
//progressive override
|
|
161
|
+
// progressive override
|
|
155
162
|
joinField.addArgument('overrideLabel', schema.stringType());
|
|
156
163
|
}
|
|
164
|
+
|
|
165
|
+
if (this.version.gte(new FeatureVersion(0, 5))) {
|
|
166
|
+
const fieldValue = this.addScalarType(schema, 'FieldValue');
|
|
167
|
+
|
|
168
|
+
// set context
|
|
169
|
+
// there are no renames that happen within the join spec, so this is fine
|
|
170
|
+
// note that join spec will only used in supergraph schema
|
|
171
|
+
const contextArgumentsType = schema.addType(new InputObjectType('join__ContextArgument'));
|
|
172
|
+
contextArgumentsType.addField('name', new NonNullType(schema.stringType()));
|
|
173
|
+
contextArgumentsType.addField('type', new NonNullType(schema.stringType()));
|
|
174
|
+
contextArgumentsType.addField('context', new NonNullType(schema.stringType()));
|
|
175
|
+
contextArgumentsType.addField('selection', new NonNullType(fieldValue));
|
|
176
|
+
|
|
177
|
+
joinField.addArgument('contextArguments', new ListType(new NonNullType(contextArgumentsType)));
|
|
178
|
+
}
|
|
157
179
|
|
|
158
180
|
if (this.isV01()) {
|
|
159
181
|
const joinOwner = this.addDirective(schema, 'owner').addLocations(DirectiveLocation.OBJECT);
|
|
@@ -261,10 +283,12 @@ export class JoinSpecDefinition extends FeatureDefinition {
|
|
|
261
283
|
// - 0.2: this is the original version released with federation 2.
|
|
262
284
|
// - 0.3: adds the `isInterfaceObject` argument to `@join__type`, and make the `graph` in `@join__field` skippable.
|
|
263
285
|
// - 0.4: adds the optional `overrideLabel` argument to `@join_field` for progressive override.
|
|
286
|
+
// - 0.5: adds the `contextArguments` argument to `@join_field` for setting context.
|
|
264
287
|
export const JOIN_VERSIONS = new FeatureDefinitions<JoinSpecDefinition>(joinIdentity)
|
|
265
288
|
.add(new JoinSpecDefinition(new FeatureVersion(0, 1)))
|
|
266
289
|
.add(new JoinSpecDefinition(new FeatureVersion(0, 2)))
|
|
267
290
|
.add(new JoinSpecDefinition(new FeatureVersion(0, 3), new FeatureVersion(2, 0)))
|
|
268
|
-
.add(new JoinSpecDefinition(new FeatureVersion(0, 4), new FeatureVersion(2, 7)))
|
|
291
|
+
.add(new JoinSpecDefinition(new FeatureVersion(0, 4), new FeatureVersion(2, 7)))
|
|
292
|
+
.add(new JoinSpecDefinition(new FeatureVersion(0, 5), new FeatureVersion(2, 8)));
|
|
269
293
|
|
|
270
294
|
registerKnownFeature(JOIN_VERSIONS);
|