Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@apollo/composition

Package Overview
Dependencies
Maintainers
1
Versions
131
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@apollo/composition - npm Package Compare versions

Comparing version 2.8.0-connectors.5 to 2.8.0

2

dist/compose.js

@@ -31,3 +31,3 @@ "use strict";

const federatedQueryGraph = (0, query_graphs_1.buildFederatedQueryGraph)(supergraph, false);
const { errors, hints } = (0, validate_1.validateGraphComposition)(supergraph.schema, supergraphQueryGraph, federatedQueryGraph);
const { errors, hints } = (0, validate_1.validateGraphComposition)(supergraph.schema, supergraph.subgraphNameToGraphEnumValue(), supergraphQueryGraph, federatedQueryGraph);
if (errors) {

@@ -34,0 +34,0 @@ return { errors };

@@ -27,2 +27,3 @@ "use strict";

'https://specs.apollo.dev/source',
'https://specs.apollo.dev/context',
];

@@ -103,2 +104,3 @@ class ComposeDirectiveManager {

sg.metadata().policyDirective(),
sg.metadata().contextDirective(),
].map(d => d.name);

@@ -105,0 +107,0 @@ if (directivesComposedByDefault.includes(directive.name)) {

@@ -45,2 +45,3 @@ import { NamedSchemaElement, SubgraphASTNode } from "@apollo/federation-internals";

IMPLICITLY_UPGRADED_FEDERATION_VERSION: HintCodeDefinition;
CONTEXTUAL_ARGUMENT_NOT_CONTEXTUAL_IN_ALL_SUBGRAPHS: HintCodeDefinition;
};

@@ -47,0 +48,0 @@ export declare class CompositionHint {

@@ -169,2 +169,7 @@ "use strict";

});
const CONTEXTUAL_ARGUMENT_NOT_CONTEXTUAL_IN_ALL_SUBGRAPHS = makeCodeDefinition({
code: 'CONTEXTUAL_ARGUMENT_NOT_CONTEXTUAL_IN_ALL_SUBGRAPHS',
level: HintLevel.INFO,
description: 'Indicates that the argument will not be present in the supergraph because it is contextual in at least one subgraph.'
});
exports.HINTS = {

@@ -200,2 +205,3 @@ INCONSISTENT_BUT_COMPATIBLE_FIELD_TYPE,

IMPLICITLY_UPGRADED_FEDERATION_VERSION,
CONTEXTUAL_ARGUMENT_NOT_CONTEXTUAL_IN_ALL_SUBGRAPHS,
};

@@ -202,0 +208,0 @@ class CompositionHint {

@@ -11,24 +11,29 @@ import { CompositeType, FieldDefinition, Operation, Schema, SchemaRootKind } from "@apollo/federation-internals";

}
export declare function validateGraphComposition(supergraphSchema: Schema, supergraphAPI: QueryGraph, federatedQueryGraph: QueryGraph): {
export declare function validateGraphComposition(supergraphSchema: Schema, subgraphNameToGraphEnumValue: Map<string, string>, supergraphAPI: QueryGraph, federatedQueryGraph: QueryGraph): {
errors?: GraphQLError[];
hints?: CompositionHint[];
};
export declare function computeSubgraphPaths(supergraphSchema: Schema, supergraphPath: RootPath<Transition>, federatedQueryGraph: QueryGraph, overrideConditions: Map<string, boolean>): {
traversal?: ValidationState;
isComplete?: boolean;
error?: GraphQLError;
};
export declare function extractValidationError(error: any): ValidationError | undefined;
export declare class ValidationContext {
readonly supergraphSchema: Schema;
readonly subgraphNameToGraphEnumValue: Map<string, string>;
private readonly joinTypeDirective;
private readonly joinFieldDirective;
constructor(supergraphSchema: Schema);
private readonly typesToContexts;
constructor(supergraphSchema: Schema, subgraphNameToGraphEnumValue: Map<string, string>);
isShareable(field: FieldDefinition<CompositeType>): boolean;
matchingContexts(typeName: string): string[];
}
type SubgraphPathInfo = {
path: TransitionPathWithLazyIndirectPaths<RootVertex>;
contexts: Map<string, {
subgraphName: string;
typeName: string;
}>;
};
export declare class ValidationState {
readonly supergraphPath: RootPath<Transition>;
readonly subgraphPaths: TransitionPathWithLazyIndirectPaths<RootVertex>[];
readonly subgraphPathInfos: SubgraphPathInfo[];
selectedOverrideConditions: Map<string, boolean>;
constructor(supergraphPath: RootPath<Transition>, subgraphPaths: TransitionPathWithLazyIndirectPaths<RootVertex>[], selectedOverrideConditions?: Map<string, boolean>);
constructor(supergraphPath: RootPath<Transition>, subgraphPathInfos: SubgraphPathInfo[], selectedOverrideConditions?: Map<string, boolean>);
static initial({ supergraphAPI, kind, federatedQueryGraph, conditionResolver, overrideConditions, }: {

@@ -41,3 +46,3 @@ supergraphAPI: QueryGraph;

}): ValidationState;
validateTransition(context: ValidationContext, supergraphEdge: Edge): {
validateTransition(context: ValidationContext, supergraphEdge: Edge, matchingContexts: string[]): {
state?: ValidationState;

@@ -48,2 +53,3 @@ error?: GraphQLError;

currentSubgraphNames(): string[];
currentSubgraphContextKeys(subgraphNameToGraphEnumValue: Map<string, string>): Set<string>;
currentSubgraphs(): {

@@ -55,2 +61,3 @@ name: string;

}
export {};
//# sourceMappingURL=validate.d.ts.map
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ValidationState = exports.ValidationContext = exports.extractValidationError = exports.computeSubgraphPaths = exports.validateGraphComposition = exports.ValidationError = void 0;
exports.ValidationState = exports.ValidationContext = exports.extractValidationError = exports.validateGraphComposition = exports.ValidationError = void 0;
const federation_internals_1 = require("@apollo/federation-internals");

@@ -44,3 +44,3 @@ const query_graphs_1 = require("@apollo/query-graphs");

+ `\nThis is not allowed as shared fields must resolve the same way in all subgraphs, and that imply at least some common runtime types between the subgraphs.`;
const error = new ValidationError(message, invalidState.supergraphPath, invalidState.subgraphPaths.map((p) => p.path), witness);
const error = new ValidationError(message, invalidState.supergraphPath, invalidState.subgraphPathInfos.map((p) => p.path.path), witness);
return federation_internals_1.ERRORS.SHAREABLE_HAS_MISMATCHED_RUNTIME_TYPES.err(error.message, {

@@ -171,36 +171,7 @@ nodes: subgraphNodes(invalidState, (s) => { var _a, _b; return (_b = (_a = s.type(field.parent.name)) === null || _a === void 0 ? void 0 : _a.field(field.name)) === null || _b === void 0 ? void 0 : _b.sourceAST; }),

}
function validateGraphComposition(supergraphSchema, supergraphAPI, federatedQueryGraph) {
const { errors, hints } = new ValidationTraversal(supergraphSchema, supergraphAPI, federatedQueryGraph).validate();
function validateGraphComposition(supergraphSchema, subgraphNameToGraphEnumValue, supergraphAPI, federatedQueryGraph) {
const { errors, hints } = new ValidationTraversal(supergraphSchema, subgraphNameToGraphEnumValue, supergraphAPI, federatedQueryGraph).validate();
return errors.length > 0 ? { errors, hints } : { hints };
}
exports.validateGraphComposition = validateGraphComposition;
function computeSubgraphPaths(supergraphSchema, supergraphPath, federatedQueryGraph, overrideConditions) {
try {
(0, federation_internals_1.assert)(!supergraphPath.hasAnyEdgeConditions(), () => `A supergraph path should not have edge condition paths (as supergraph edges should not have conditions): ${supergraphPath}`);
const conditionResolver = (0, query_graphs_1.simpleValidationConditionResolver)({ supergraph: supergraphSchema, queryGraph: federatedQueryGraph, withCaching: true });
const initialState = ValidationState.initial({ supergraphAPI: supergraphPath.graph, kind: supergraphPath.root.rootKind, federatedQueryGraph, conditionResolver, overrideConditions });
const context = new ValidationContext(supergraphSchema);
let state = initialState;
let isIncomplete = false;
for (const [edge] of supergraphPath) {
const { state: updated, error } = state.validateTransition(context, edge);
if (error) {
throw error;
}
if (!updated) {
isIncomplete = true;
break;
}
state = updated;
}
return { traversal: state, isComplete: !isIncomplete };
}
catch (error) {
if (error instanceof graphql_1.GraphQLError) {
return { error };
}
throw error;
}
}
exports.computeSubgraphPaths = computeSubgraphPaths;
function initialSubgraphPaths(kind, subgraphs) {

@@ -226,7 +197,38 @@ const root = subgraphs.root(kind);

class ValidationContext {
constructor(supergraphSchema) {
constructor(supergraphSchema, subgraphNameToGraphEnumValue) {
var _a, _b;
this.supergraphSchema = supergraphSchema;
this.subgraphNameToGraphEnumValue = subgraphNameToGraphEnumValue;
const [_, joinSpec] = (0, federation_internals_1.validateSupergraph)(supergraphSchema);
this.joinTypeDirective = joinSpec.typeDirective(supergraphSchema);
this.joinFieldDirective = joinSpec.fieldDirective(supergraphSchema);
this.typesToContexts = new Map();
let contextDirective;
const contextFeature = (_a = supergraphSchema.coreFeatures) === null || _a === void 0 ? void 0 : _a.getByIdentity(federation_internals_1.ContextSpecDefinition.identity);
if (contextFeature) {
const contextSpec = federation_internals_1.CONTEXT_VERSIONS.find(contextFeature.url.version);
(0, federation_internals_1.assert)(contextSpec, `Unexpected context spec version ${contextFeature.url.version}`);
contextDirective = contextSpec.contextDirective(supergraphSchema);
}
for (const application of (_b = contextDirective === null || contextDirective === void 0 ? void 0 : contextDirective.applications()) !== null && _b !== void 0 ? _b : []) {
const { name: context } = application.arguments();
(0, federation_internals_1.assert)(application.parent instanceof federation_internals_1.NamedSchemaElement, "Unexpectedly found unnamed element with @context");
const type = supergraphSchema.type(application.parent.name);
(0, federation_internals_1.assert)(type, `Type ${application.parent.name} unexpectedly doesn't exist`);
const typeNames = [type.name];
if ((0, federation_internals_1.isInterfaceType)(type)) {
typeNames.push(...type.allImplementations().map((t) => t.name));
}
else if ((0, federation_internals_1.isUnionType)(type)) {
typeNames.push(...type.types().map((t) => t.name));
}
for (const typeName of typeNames) {
if (this.typesToContexts.has(typeName)) {
this.typesToContexts.get(typeName).add(context);
}
else {
this.typesToContexts.set(typeName, new Set([context]));
}
}
}
}

@@ -249,18 +251,25 @@ isShareable(field) {

}
matchingContexts(typeName) {
var _a;
return [...((_a = this.typesToContexts.get(typeName)) !== null && _a !== void 0 ? _a : [])];
}
}
exports.ValidationContext = ValidationContext;
class ValidationState {
constructor(supergraphPath, subgraphPaths, selectedOverrideConditions = new Map()) {
constructor(supergraphPath, subgraphPathInfos, selectedOverrideConditions = new Map()) {
this.supergraphPath = supergraphPath;
this.subgraphPaths = subgraphPaths;
this.subgraphPathInfos = subgraphPathInfos;
this.selectedOverrideConditions = selectedOverrideConditions;
}
static initial({ supergraphAPI, kind, federatedQueryGraph, conditionResolver, overrideConditions, }) {
return new ValidationState(query_graphs_1.GraphPath.fromGraphRoot(supergraphAPI, kind), initialSubgraphPaths(kind, federatedQueryGraph).map((p) => query_graphs_1.TransitionPathWithLazyIndirectPaths.initial(p, conditionResolver, overrideConditions)));
return new ValidationState(query_graphs_1.GraphPath.fromGraphRoot(supergraphAPI, kind), initialSubgraphPaths(kind, federatedQueryGraph).map((p) => query_graphs_1.TransitionPathWithLazyIndirectPaths.initial(p, conditionResolver, overrideConditions)).map((p) => ({
path: p,
contexts: new Map(),
})));
}
validateTransition(context, supergraphEdge) {
validateTransition(context, supergraphEdge, matchingContexts) {
(0, federation_internals_1.assert)(!supergraphEdge.conditions, () => `Supergraph edges should not have conditions (${supergraphEdge})`);
const transition = supergraphEdge.transition;
const targetType = supergraphEdge.tail.type;
const newSubgraphPaths = [];
const newSubgraphPathInfos = [];
const deadEnds = [];

@@ -271,3 +280,3 @@ const newOverrideConditions = new Map([...this.selectedOverrideConditions]);

}
for (const path of this.subgraphPaths) {
for (const { path, contexts } of this.subgraphPathInfos) {
const options = (0, query_graphs_1.advancePathWithTransition)(path, transition, targetType, newOverrideConditions);

@@ -281,15 +290,27 @@ if ((0, query_graphs_1.isUnadvanceable)(options)) {

}
newSubgraphPaths.push(...options);
let newContexts = contexts;
if (matchingContexts.length) {
const subgraphName = path.path.tail.source;
const typeName = path.path.tail.type.name;
newContexts = new Map([...contexts]);
for (const matchingContext in matchingContexts) {
newContexts.set(matchingContext, {
subgraphName,
typeName,
});
}
}
newSubgraphPathInfos.push(...options.map((p) => ({ path: p, contexts: newContexts })));
}
const newPath = this.supergraphPath.add(transition, supergraphEdge, query_graphs_1.noConditionsResolution);
if (newSubgraphPaths.length === 0) {
return { error: satisfiabilityError(newPath, this.subgraphPaths.map((p) => p.path), deadEnds) };
if (newSubgraphPathInfos.length === 0) {
return { error: satisfiabilityError(newPath, this.subgraphPathInfos.map((p) => p.path.path), deadEnds) };
}
const updatedState = new ValidationState(newPath, newSubgraphPaths, newOverrideConditions);
const updatedState = new ValidationState(newPath, newSubgraphPathInfos, newOverrideConditions);
let hint = undefined;
if (newSubgraphPaths.length > 1
if (newSubgraphPathInfos.length > 1
&& transition.kind === 'FieldCollection'
&& (0, federation_internals_1.isAbstractType)(newPath.tail.type)
&& context.isShareable(transition.definition)) {
const filteredPaths = newSubgraphPaths.map((p) => p.path).filter((p) => (0, federation_internals_1.isAbstractType)(p.tail.type));
const filteredPaths = newSubgraphPathInfos.map((p) => p.path.path).filter((p) => (0, federation_internals_1.isAbstractType)(p.tail.type));
if (filteredPaths.length > 1) {

@@ -301,3 +322,3 @@ const allRuntimeTypes = possibleRuntimeTypeNamesSorted(newPath);

let hasAllEmpty = true;
for (const path of newSubgraphPaths) {
for (const { path } of newSubgraphPathInfos) {
const subgraph = path.path.tail.source;

@@ -328,4 +349,4 @@ const typeNames = possibleRuntimeTypeNamesSorted(path.path);

const subgraphs = [];
for (const path of this.subgraphPaths) {
const source = path.path.tail.source;
for (const pathInfo of this.subgraphPathInfos) {
const source = pathInfo.path.path.tail.source;
if (!subgraphs.includes(source)) {

@@ -337,11 +358,27 @@ subgraphs.push(source);

}
currentSubgraphContextKeys(subgraphNameToGraphEnumValue) {
const subgraphContextKeys = new Set();
for (const pathInfo of this.subgraphPathInfos) {
const tailSubgraphName = pathInfo.path.path.tail.source;
const tailSubgraphEnumValue = subgraphNameToGraphEnumValue.get(tailSubgraphName);
const entryKeys = [];
const contexts = Array.from(pathInfo.contexts.entries());
contexts.sort((a, b) => a[0].localeCompare(b[0]));
for (const [context, { subgraphName, typeName }] of contexts) {
const subgraphEnumValue = subgraphNameToGraphEnumValue.get(subgraphName);
entryKeys.push(`${context}=${subgraphEnumValue}.${typeName}`);
}
subgraphContextKeys.add(`${tailSubgraphEnumValue}[${entryKeys.join(',')}]`);
}
return subgraphContextKeys;
}
currentSubgraphs() {
if (this.subgraphPaths.length === 0) {
if (this.subgraphPathInfos.length === 0) {
return [];
}
const sources = this.subgraphPaths[0].path.graph.sources;
const sources = this.subgraphPathInfos[0].path.path.graph.sources;
return this.currentSubgraphNames().map((name) => ({ name, schema: sources.get(name) }));
}
toString() {
return `${this.supergraphPath} <=> [${this.subgraphPaths.map(s => s.toString()).join(', ')}]`;
return `${this.supergraphPath} <=> [${this.subgraphPathInfos.map(s => s.path.toString()).join(', ')}]`;
}

@@ -351,3 +388,4 @@ }

function isSupersetOrEqual(maybeSuperset, other) {
const includesAllSubgraphs = other.subgraphs.every((s) => maybeSuperset.subgraphs.includes(s));
const includesAllSubgraphs = [...other.subgraphContextKeys]
.every((s) => maybeSuperset.subgraphContextKeys.has(s));
const includesAllOverrideConditions = [...other.overrideConditions.entries()].every(([label, value]) => maybeSuperset.overrideConditions.get(label) === value);

@@ -357,3 +395,3 @@ return includesAllSubgraphs && includesAllOverrideConditions;

class ValidationTraversal {
constructor(supergraphSchema, supergraphAPI, federatedQueryGraph) {
constructor(supergraphSchema, subgraphNameToGraphEnumValue, supergraphAPI, federatedQueryGraph) {
this.stack = [];

@@ -375,3 +413,3 @@ this.validationErrors = [];

this.previousVisits = new query_graphs_1.QueryGraphState(supergraphAPI);
this.context = new ValidationContext(supergraphSchema);
this.context = new ValidationContext(supergraphSchema, subgraphNameToGraphEnumValue);
}

@@ -389,3 +427,3 @@ validate() {

const currentVertexVisit = {
subgraphs: state.currentSubgraphNames(),
subgraphContextKeys: state.currentSubgraphContextKeys(this.context.subgraphNameToGraphEnumValue),
overrideConditions: state.selectedOverrideConditions

@@ -416,4 +454,7 @@ };

}
const matchingContexts = edge.transition.kind === 'FieldCollection'
? this.context.matchingContexts(edge.head.type.name)
: [];
debug.group(() => `Validating supergraph edge ${edge}`);
const { state: newState, error, hint } = state.validateTransition(this.context, edge);
const { state: newState, error, hint } = state.validateTransition(this.context, edge, matchingContexts);
if (error) {

@@ -420,0 +461,0 @@ debug.groupEnd(`Validation error!`);

{
"name": "@apollo/composition",
"version": "2.8.0-connectors.5",
"version": "2.8.0",
"description": "Apollo Federation composition utilities",

@@ -30,4 +30,4 @@ "main": "dist/index.js",

"dependencies": {
"@apollo/federation-internals": "2.8.0-connectors.5",
"@apollo/query-graphs": "2.8.0-connectors.5"
"@apollo/federation-internals": "2.8.0",
"@apollo/query-graphs": "2.8.0"
},

@@ -34,0 +34,0 @@ "peerDependencies": {

@@ -73,3 +73,8 @@ import {

const federatedQueryGraph = buildFederatedQueryGraph(supergraph, false);
const { errors, hints } = validateGraphComposition(supergraph.schema, supergraphQueryGraph, federatedQueryGraph);
const { errors, hints } = validateGraphComposition(
supergraph.schema,
supergraph.subgraphNameToGraphEnumValue(),
supergraphQueryGraph,
federatedQueryGraph
);
if (errors) {

@@ -76,0 +81,0 @@ return { errors };

@@ -68,2 +68,3 @@ import {

'https://specs.apollo.dev/source',
'https://specs.apollo.dev/context',
];

@@ -180,2 +181,3 @@

sg.metadata().policyDirective(),
sg.metadata().contextDirective(),
].map(d => d.name);

@@ -182,0 +184,0 @@ if (directivesComposedByDefault.includes(directive.name)) {

@@ -215,2 +215,8 @@ import { NamedSchemaElement, SubgraphASTNode } from "@apollo/federation-internals";

const CONTEXTUAL_ARGUMENT_NOT_CONTEXTUAL_IN_ALL_SUBGRAPHS = makeCodeDefinition({
code: 'CONTEXTUAL_ARGUMENT_NOT_CONTEXTUAL_IN_ALL_SUBGRAPHS',
level: HintLevel.INFO,
description: 'Indicates that the argument will not be present in the supergraph because it is contextual in at least one subgraph.'
});
export const HINTS = {

@@ -246,2 +252,3 @@ INCONSISTENT_BUT_COMPATIBLE_FIELD_TYPE,

IMPLICITLY_UPGRADED_FEDERATION_VERSION,
CONTEXTUAL_ARGUMENT_NOT_CONTEXTUAL_IN_ALL_SUBGRAPHS,
}

@@ -248,0 +255,0 @@

@@ -15,5 +15,7 @@ import {

isDefined,
isInterfaceType,
isLeafType,
isNullableType,
isObjectType,
isUnionType,
joinStrings,

@@ -38,2 +40,5 @@ MultiMap,

JoinFieldDirectiveArguments,
ContextSpecDefinition,
CONTEXT_VERSIONS,
NamedSchemaElement,
} from "@apollo/federation-internals";

@@ -111,3 +116,3 @@ import {

+ `\nThis is not allowed as shared fields must resolve the same way in all subgraphs, and that imply at least some common runtime types between the subgraphs.`;
const error = new ValidationError(message, invalidState.supergraphPath, invalidState.subgraphPaths.map((p) => p.path), witness);
const error = new ValidationError(message, invalidState.supergraphPath, invalidState.subgraphPathInfos.map((p) => p.path.path), witness);
return ERRORS.SHAREABLE_HAS_MISMATCHED_RUNTIME_TYPES.err(error.message, {

@@ -307,2 +312,3 @@ nodes: subgraphNodes(invalidState, (s) => (s.type(field.parent.name) as CompositeType | undefined)?.field(field.name)?.sourceAST),

supergraphSchema: Schema,
subgraphNameToGraphEnumValue: Map<string, string>,
supergraphAPI: QueryGraph,

@@ -314,44 +320,11 @@ federatedQueryGraph: QueryGraph,

} {
const { errors, hints } = new ValidationTraversal(supergraphSchema, supergraphAPI, federatedQueryGraph).validate();
const { errors, hints } = new ValidationTraversal(
supergraphSchema,
subgraphNameToGraphEnumValue,
supergraphAPI,
federatedQueryGraph,
).validate();
return errors.length > 0 ? { errors, hints } : { hints };
}
// TODO: we don't use this anywhere, can we just remove it?
export function computeSubgraphPaths(
supergraphSchema: Schema,
supergraphPath: RootPath<Transition>,
federatedQueryGraph: QueryGraph,
overrideConditions: Map<string, boolean>,
): {
traversal?: ValidationState,
isComplete?: boolean,
error?: GraphQLError
} {
try {
assert(!supergraphPath.hasAnyEdgeConditions(), () => `A supergraph path should not have edge condition paths (as supergraph edges should not have conditions): ${supergraphPath}`);
const conditionResolver = simpleValidationConditionResolver({ supergraph: supergraphSchema, queryGraph: federatedQueryGraph, withCaching: true });
const initialState = ValidationState.initial({ supergraphAPI: supergraphPath.graph, kind: supergraphPath.root.rootKind, federatedQueryGraph, conditionResolver, overrideConditions });
const context = new ValidationContext(supergraphSchema);
let state = initialState;
let isIncomplete = false;
for (const [edge] of supergraphPath) {
const { state: updated, error } = state.validateTransition(context, edge);
if (error) {
throw error;
}
if (!updated) {
isIncomplete = true;
break;
}
state = updated;
}
return {traversal: state, isComplete: !isIncomplete};
} catch (error) {
if (error instanceof GraphQLError) {
return {error};
}
throw error;
}
}
function initialSubgraphPaths(kind: SchemaRootKind, subgraphs: QueryGraph): RootPath<Transition>[] {

@@ -383,5 +356,7 @@ const root = subgraphs.root(kind);

private readonly joinFieldDirective: DirectiveDefinition<JoinFieldDirectiveArguments>;
private readonly typesToContexts: Map<string, Set<string>>
constructor(
readonly supergraphSchema: Schema,
readonly subgraphNameToGraphEnumValue: Map<string, string>,
) {

@@ -391,2 +366,34 @@ const [_, joinSpec] = validateSupergraph(supergraphSchema);

this.joinFieldDirective = joinSpec.fieldDirective(supergraphSchema);
this.typesToContexts = new Map();
let contextDirective: DirectiveDefinition<{ name: string }> | undefined;
const contextFeature = supergraphSchema.coreFeatures?.getByIdentity(ContextSpecDefinition.identity);
if (contextFeature) {
const contextSpec = CONTEXT_VERSIONS.find(contextFeature.url.version);
assert(contextSpec, `Unexpected context spec version ${contextFeature.url.version}`);
contextDirective = contextSpec.contextDirective(supergraphSchema);
}
for (const application of contextDirective?.applications() ?? []) {
const { name: context } = application.arguments();
assert(
application.parent instanceof NamedSchemaElement,
"Unexpectedly found unnamed element with @context"
);
const type = supergraphSchema.type(application.parent.name);
assert(type, `Type ${application.parent.name} unexpectedly doesn't exist`);
const typeNames = [type.name];
if (isInterfaceType(type)) {
typeNames.push(...type.allImplementations().map((t) => t.name));
} else if (isUnionType(type)) {
typeNames.push(...type.types().map((t) => t.name));
}
for (const typeName of typeNames) {
if (this.typesToContexts.has(typeName)) {
this.typesToContexts.get(typeName)!.add(context);
} else {
this.typesToContexts.set(typeName, new Set([context]));
}
}
}
}

@@ -414,4 +421,14 @@

}
matchingContexts(typeName: string): string[] {
return [...(this.typesToContexts.get(typeName) ?? [])];
}
}
type SubgraphPathInfo = {
path: TransitionPathWithLazyIndirectPaths<RootVertex>,
// The key for this map is the context name in the supergraph schema.
contexts: Map<string, { subgraphName: string, typeName: string }>,
}
export class ValidationState {

@@ -422,3 +439,3 @@ constructor(

// All the possible paths we could be in the subgraph.
public readonly subgraphPaths: TransitionPathWithLazyIndirectPaths<RootVertex>[],
public readonly subgraphPathInfos: SubgraphPathInfo[],
// When we encounter an `@override`n field with a label condition, we record

@@ -453,3 +470,6 @@ // its value (T/F) as we traverse the graph. This allows us to ignore paths

),
),
).map((p) => ({
path: p,
contexts: new Map(),
})),
);

@@ -469,3 +489,3 @@ }

*/
validateTransition(context: ValidationContext, supergraphEdge: Edge): {
validateTransition(context: ValidationContext, supergraphEdge: Edge, matchingContexts: string[]): {
state?: ValidationState,

@@ -479,3 +499,3 @@ error?: GraphQLError,

const targetType = supergraphEdge.tail.type;
const newSubgraphPaths: TransitionPathWithLazyIndirectPaths<RootVertex>[] = [];
const newSubgraphPathInfos: SubgraphPathInfo[] = [];
const deadEnds: Unadvanceables[] = [];

@@ -492,3 +512,3 @@ // If the edge has an override condition, we should capture it in the state so

for (const path of this.subgraphPaths) {
for (const { path, contexts } of this.subgraphPathInfos) {
const options = advancePathWithTransition(

@@ -509,7 +529,23 @@ path,

}
newSubgraphPaths.push(...options);
let newContexts = contexts;
if (matchingContexts.length) {
const subgraphName = path.path.tail.source;
const typeName = path.path.tail.type.name;
newContexts = new Map([...contexts]);
for (const matchingContext in matchingContexts) {
newContexts.set(
matchingContext,
{
subgraphName,
typeName,
}
)
}
}
newSubgraphPathInfos.push(...options.map((p) => ({ path: p, contexts: newContexts })));
}
const newPath = this.supergraphPath.add(transition, supergraphEdge, noConditionsResolution);
if (newSubgraphPaths.length === 0) {
return { error: satisfiabilityError(newPath, this.subgraphPaths.map((p) => p.path), deadEnds) };
if (newSubgraphPathInfos.length === 0) {
return { error: satisfiabilityError(newPath, this.subgraphPathInfos.map((p) => p.path.path), deadEnds) };
}

@@ -519,3 +555,3 @@

newPath,
newSubgraphPaths,
newSubgraphPathInfos,
newOverrideConditions,

@@ -542,3 +578,3 @@ );

if (
newSubgraphPaths.length > 1
newSubgraphPathInfos.length > 1
&& transition.kind === 'FieldCollection'

@@ -548,3 +584,3 @@ && isAbstractType(newPath.tail.type)

) {
const filteredPaths = newSubgraphPaths.map((p) => p.path).filter((p) => isAbstractType(p.tail.type));
const filteredPaths = newSubgraphPathInfos.map((p) => p.path.path).filter((p) => isAbstractType(p.tail.type));
if (filteredPaths.length > 1) {

@@ -560,3 +596,3 @@ // We start our intersection by using all the supergraph types, both because it's a convenient "max" set to start our intersection,

let hasAllEmpty = true;
for (const path of newSubgraphPaths) {
for (const { path } of newSubgraphPathInfos) {
const subgraph = path.path.tail.source;

@@ -600,4 +636,4 @@ const typeNames = possibleRuntimeTypeNamesSorted(path.path);

const subgraphs: string[] = [];
for (const path of this.subgraphPaths) {
const source = path.path.tail.source;
for (const pathInfo of this.subgraphPathInfos) {
const source = pathInfo.path.path.tail.source;
if (!subgraphs.includes(source)) {

@@ -610,7 +646,26 @@ subgraphs.push(source);

currentSubgraphContextKeys(subgraphNameToGraphEnumValue: Map<string, string>): Set<string> {
const subgraphContextKeys: Set<string> = new Set();
for (const pathInfo of this.subgraphPathInfos) {
const tailSubgraphName = pathInfo.path.path.tail.source;
const tailSubgraphEnumValue = subgraphNameToGraphEnumValue.get(tailSubgraphName);
const entryKeys = [];
const contexts = Array.from(pathInfo.contexts.entries());
contexts.sort((a, b) => a[0].localeCompare(b[0]));
for (const [context, { subgraphName, typeName }] of contexts) {
const subgraphEnumValue = subgraphNameToGraphEnumValue.get(subgraphName);
entryKeys.push(`${context}=${subgraphEnumValue}.${typeName}`);
}
subgraphContextKeys.add(
`${tailSubgraphEnumValue}[${entryKeys.join(',')}]`
);
}
return subgraphContextKeys;
}
currentSubgraphs(): { name: string, schema: Schema }[] {
if (this.subgraphPaths.length === 0) {
if (this.subgraphPathInfos.length === 0) {
return [];
}
const sources = this.subgraphPaths[0].path.graph.sources;
const sources = this.subgraphPathInfos[0].path.path.graph.sources;
return this.currentSubgraphNames().map((name) => ({ name, schema: sources.get(name)!}));

@@ -620,3 +675,3 @@ }

toString(): string {
return `${this.supergraphPath} <=> [${this.subgraphPaths.map(s => s.toString()).join(', ')}]`;
return `${this.supergraphPath} <=> [${this.subgraphPathInfos.map(s => s.path.toString()).join(', ')}]`;
}

@@ -628,3 +683,4 @@ }

function isSupersetOrEqual(maybeSuperset: VertexVisit, other: VertexVisit): boolean {
const includesAllSubgraphs = other.subgraphs.every((s) => maybeSuperset.subgraphs.includes(s));
const includesAllSubgraphs = [...other.subgraphContextKeys]
.every((s) => maybeSuperset.subgraphContextKeys.has(s));
const includesAllOverrideConditions = [...other.overrideConditions.entries()].every(([label, value]) =>

@@ -638,3 +694,3 @@ maybeSuperset.overrideConditions.get(label) === value

interface VertexVisit {
subgraphs: string[];
subgraphContextKeys: Set<string>;
overrideConditions: Map<string, boolean>;

@@ -659,2 +715,3 @@ }

supergraphSchema: Schema,
subgraphNameToGraphEnumValue: Map<string, string>,
supergraphAPI: QueryGraph,

@@ -676,3 +733,6 @@ federatedQueryGraph: QueryGraph,

this.previousVisits = new QueryGraphState(supergraphAPI);
this.context = new ValidationContext(supergraphSchema);
this.context = new ValidationContext(
supergraphSchema,
subgraphNameToGraphEnumValue,
);
}

@@ -695,3 +755,3 @@

const currentVertexVisit: VertexVisit = {
subgraphs: state.currentSubgraphNames(),
subgraphContextKeys: state.currentSubgraphContextKeys(this.context.subgraphNameToGraphEnumValue),
overrideConditions: state.selectedOverrideConditions

@@ -740,5 +800,8 @@ };

const matchingContexts = edge.transition.kind === 'FieldCollection'
? this.context.matchingContexts(edge.head.type.name)
: [];
debug.group(() => `Validating supergraph edge ${edge}`);
const { state: newState, error, hint } = state.validateTransition(this.context, edge);
const { state: newState, error, hint } = state.validateTransition(this.context, edge, matchingContexts);
if (error) {

@@ -745,0 +808,0 @@ debug.groupEnd(`Validation error!`);

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc