Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
268 changes: 134 additions & 134 deletions composition-go/index.global.js

Large diffs are not rendered by default.

7 changes: 6 additions & 1 deletion composition/src/resolvability-graph/graph-nodes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export class Edge {
edgeName: string;
id: number;
isAbstractEdge: boolean;
isExternal = false;
isInaccessible = false;
node: GraphNode;
visitedIndices = new Set<number>();
Expand All @@ -16,6 +17,10 @@ export class Edge {
this.isAbstractEdge = isAbstractEdge;
this.node = node;
}

isEdgeInaccessible(): boolean {
return this.isInaccessible || this.node.isInaccessible;
}
}

export type GraphNodeOptions = {
Expand All @@ -24,6 +29,7 @@ export type GraphNodeOptions = {
};

export class GraphNode {
externalFieldSets = new Set<string>();
fieldDataByName = new Map<FieldName, GraphFieldData>();
headToTailEdges = new Map<string, Edge>();
entityEdges = new Array<Edge>();
Expand Down Expand Up @@ -79,7 +85,6 @@ export class GraphNode {
export class RootNode {
fieldDataByName = new Map<FieldName, GraphFieldData>();
headToSharedTailEdges = new Map<string, Array<Edge>>();
// It is used
isAbstract = false;
isRootNode = true;
typeName: TypeName;
Expand Down
15 changes: 15 additions & 0 deletions composition/src/resolvability-graph/graph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,10 @@ export class Graph {
}
node.hasEntitySiblings = true;
for (const fieldSet of node.satisfiedFieldSets) {
// If the field set is unresolvable in the entity's own subgraph, it cannot be used to jump to other subgraphs.
if (node.externalFieldSets.has(fieldSet)) {
continue;
}
const subgraphNames = entityDataNode.targetSubgraphNamesByFieldSet.get(fieldSet);
for (const subgraphName of subgraphNames ?? []) {
// A subgraph should not jump to itself
Expand All @@ -141,6 +145,7 @@ export class Graph {
entityNodeName,
relativeOriginPaths,
resDataByRelativeOriginPath,
resolvedPaths,
subgraphNameByUnresolvablePath,
visitedEntities,
}: VisitEntityParams) {
Expand All @@ -159,6 +164,7 @@ export class Graph {
relativeOriginPaths,
resDataByNodeName: this.resDataByNodeName,
resDataByRelativeOriginPath,
resolvedPaths,
subgraphNameByUnresolvablePath,
visitedEntities,
});
Expand Down Expand Up @@ -194,6 +200,7 @@ export class Graph {
selectionPath: selectionPath,
}),
resDataByRelativeOriginPath,
resolvedPaths,
subgraphNameByUnresolvablePath,
visitedEntities,
});
Expand Down Expand Up @@ -248,6 +255,7 @@ export class Graph {
}
}
}

return {
success: true,
};
Expand Down Expand Up @@ -312,6 +320,7 @@ export class Graph {
}

validateSharedRootFieldEntities({ rootFieldData, walker }: ValidateEntitiesParams): ValidationResult {
const resolvedPaths = new Set<SelectionPath>();
for (const [pathFromRoot, entityNodeNames] of walker.entityNodeNamesByPath) {
const subgraphNameByUnresolvablePath = new Map<SelectionPath, SubgraphName>();
// Shared fields are unique contexts, so the resolution data cannot be reused.
Expand All @@ -324,6 +333,7 @@ export class Graph {
encounteredEntityNodeNames: new Set<NodeName>(),
entityNodeName,
resDataByRelativeOriginPath: resDataByRelativeOriginPath,
resolvedPaths,
subgraphNameByUnresolvablePath,
visitedEntities: new Set<NodeName>(),
});
Expand Down Expand Up @@ -375,6 +385,7 @@ export class Graph {
success: false,
};
}

if (walker.unresolvablePaths.size > 0) {
return {
errors: generateRootResolvabilityErrors({
Expand All @@ -385,12 +396,14 @@ export class Graph {
success: false,
};
}

return {
success: true,
};
}

validateRootFieldEntities({ rootFieldData, walker }: ValidateEntitiesParams): ValidationResult {
const resolvedPaths = new Set<SelectionPath>();
for (const [entityNodeName, entityPaths] of walker.pathsByEntityNodeName) {
const subgraphNameByUnresolvablePath = new Map<SelectionPath, SubgraphName>();
if (this.resDataByNodeName.has(entityNodeName)) {
Expand All @@ -405,6 +418,7 @@ export class Graph {
encounteredEntityNodeNames: new Set<NodeName>(),
entityNodeName,
resDataByRelativeOriginPath: resDataByRelativeOriginPath,
resolvedPaths,
subgraphNameByUnresolvablePath,
visitedEntities: getValueOrDefault(
this.visitedEntitiesByOriginEntity,
Expand All @@ -416,6 +430,7 @@ export class Graph {
if (subgraphNameByUnresolvablePath.size < 1) {
continue;
}

return {
errors: this.getEntityResolvabilityErrors({
entityNodeName,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { GraphFieldData } from '../../utils/types';
import { unexpectedEdgeFatalError } from '../../errors/errors';
import { FieldName } from '../types/types';
import { NodeResolutionDataParams } from './types/params';
import { AddExternalSubgraphNameParams, NodeResolutionDataParams } from './types/params';

export class NodeResolutionData {
#isResolved = false;
Expand All @@ -24,11 +24,11 @@ export class NodeResolutionData {
this.typeName = typeName;
}

addData(data: NodeResolutionData) {
for (const fieldName of data.resolvedFieldNames) {
addData({ resolvedDescendantNames, resolvedFieldNames }: NodeResolutionData) {
for (const fieldName of resolvedFieldNames) {
this.addResolvedFieldName(fieldName);
}
for (const fieldName of data.resolvedDescendantNames) {
for (const fieldName of resolvedDescendantNames) {
this.resolvedDescendantNames.add(fieldName);
}
}
Expand All @@ -40,6 +40,14 @@ export class NodeResolutionData {
this.resolvedFieldNames.add(fieldName);
}

addExternalSubgraphName({ fieldName, subgraphName }: AddExternalSubgraphNameParams) {
const fieldData = this.fieldDataByName.get(fieldName);
if (!fieldData) {
throw unexpectedEdgeFatalError(this.typeName, [fieldName]);
}
fieldData.externalSubgraphNames.add(subgraphName);
}

copy(): NodeResolutionData {
return new NodeResolutionData({
// Only used for reading, so just a shallow copy.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { FieldName, TypeName } from '../../types/types';
import { FieldName, SubgraphName, TypeName } from '../../types/types';
import { GraphFieldData } from '../../../utils/types';

export type NodeResolutionDataParams = {
Expand All @@ -8,3 +8,8 @@ export type NodeResolutionDataParams = {
resolvedDescendantNames?: Set<FieldName>;
resolvedFieldNames?: Set<FieldName>;
};

export type AddExternalSubgraphNameParams = {
fieldName: FieldName;
subgraphName: SubgraphName;
};
1 change: 1 addition & 0 deletions composition/src/resolvability-graph/types/params.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export type VisitEntityParams = {
encounteredEntityNodeNames: Set<NodeName>;
entityNodeName: NodeName;
resDataByRelativeOriginPath: Map<SelectionPath, NodeResolutionData>;
resolvedPaths: Set<SelectionPath>;
subgraphNameByUnresolvablePath: Map<SelectionPath, SubgraphName>;
visitedEntities: Set<NodeName>;
relativeOriginPaths?: Set<SelectionPath>;
Expand Down
1 change: 1 addition & 0 deletions composition/src/resolvability-graph/types/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export type VisitNodeResult = {
visited: boolean;
areDescendantsResolved: boolean;
isExternal?: true;
isRevisitedNode?: true;
};

Expand Down
65 changes: 48 additions & 17 deletions composition/src/resolvability-graph/utils/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,11 @@ import { SelectionSetSegments } from './types/types';
import { LITERAL_SPACE, QUOTATION_JOIN } from '../constants/string-constants';

export type UnresolvableFieldData = {
fieldName: string;
externalSubgraphNames: Set<SubgraphName>;
fieldName: FieldName;
selectionSet: string;
subgraphNames: Set<string>;
typeName: string;
subgraphNames: Set<SubgraphName>;
typeName: TypeName;
};

export function newRootFieldData(
Expand Down Expand Up @@ -55,13 +56,25 @@ export function generateResolvabilityErrorReasons({
rootFieldData,
unresolvableFieldData,
}: GenerateResolvabilityErrorReasonsParams): Array<string> {
const { fieldName, typeName, subgraphNames } = unresolvableFieldData;
const reasons: Array<string> = [
rootFieldData.message,
`The field "${typeName}.${fieldName}" is defined in the following subgraph` +
(subgraphNames.size > 1 ? `s` : ``) +
`: "${[...subgraphNames].join(QUOTATION_JOIN)}".`,
];
const { externalSubgraphNames, fieldName, typeName, subgraphNames } = unresolvableFieldData;
const reasons: Array<string> = [rootFieldData.message];
if (externalSubgraphNames.size > 0) {
const nonExternalSubgraphNames = subgraphNames.difference(externalSubgraphNames);
reasons.push(
`The field "${typeName}.${fieldName}" is defined (and resolvable) in the following subgraph` +
(nonExternalSubgraphNames.size > 1 ? `s` : ``) +
`: "${[...nonExternalSubgraphNames].join(QUOTATION_JOIN)}".`,
`The field "${typeName}.${fieldName}" is defined "@external" (and unresolvable) in the following subgraph` +
(externalSubgraphNames.size > 1 ? `s` : ``) +
`: "${[...externalSubgraphNames].join(QUOTATION_JOIN)}".`,
);
} else {
reasons.push(
`The field "${typeName}.${fieldName}" is defined in the following subgraph` +
(subgraphNames.size > 1 ? `s` : ``) +
`: "${[...subgraphNames].join(QUOTATION_JOIN)}".`,
);
}
if (entityAncestorData) {
let hasIntersectingTargetSubgraph = false;
for (const [targetSubgraphName, fieldSets] of entityAncestorData.fieldSetsByTargetSubgraphName) {
Expand All @@ -70,6 +83,9 @@ export function generateResolvabilityErrorReasons({
}
hasIntersectingTargetSubgraph = true;
for (const fieldSet of fieldSets) {
if (entityAncestorData.subgraphName === targetSubgraphName) {
continue;
}
reasons.push(
`The entity ancestor "${entityAncestorData.typeName}" in subgraph "${entityAncestorData.subgraphName}" does not satisfy the key field set "${fieldSet}" to access subgraph "${targetSubgraphName}".`,
);
Expand Down Expand Up @@ -106,13 +122,25 @@ export function generateSharedResolvabilityErrorReasons({
rootFieldData,
unresolvableFieldData,
}: GenerateSharedResolvabilityErrorReasonsParams): Array<string> {
const { fieldName, typeName, subgraphNames } = unresolvableFieldData;
const reasons: Array<string> = [
rootFieldData.message,
`The field "${typeName}.${fieldName}" is defined in the following subgraph` +
(subgraphNames.size > 1 ? `s` : ``) +
`: "${[...subgraphNames].join(QUOTATION_JOIN)}".`,
];
const { externalSubgraphNames, fieldName, typeName, subgraphNames } = unresolvableFieldData;
const reasons: Array<string> = [rootFieldData.message];
if (externalSubgraphNames.size > 0) {
const nonExternalSubgraphNames = subgraphNames.difference(externalSubgraphNames);
reasons.push(
`The field "${typeName}.${fieldName}" is defined (and resolvable) in the following subgraph` +
(nonExternalSubgraphNames.size > 1 ? `s` : ``) +
`: "${[...nonExternalSubgraphNames].join(QUOTATION_JOIN)}".`,
`The field "${typeName}.${fieldName}" is defined "@external" (and unresolvable) in the following subgraph` +
(externalSubgraphNames.size > 1 ? `s` : ``) +
`: "${[...externalSubgraphNames].join(QUOTATION_JOIN)}".`,
);
} else {
reasons.push(
`The field "${typeName}.${fieldName}" is defined in the following subgraph` +
(subgraphNames.size > 1 ? `s` : ``) +
`: "${[...subgraphNames].join(QUOTATION_JOIN)}".`,
);
}
let hasIntersectingTargetSubgraph = false;
for (const [targetSubgraphName, fieldSets] of entityAncestors.fieldSetsByTargetSubgraphName) {
if (!subgraphNames.has(targetSubgraphName)) {
Expand Down Expand Up @@ -206,6 +234,7 @@ export function generateRootResolvabilityErrors({
const selectionSetSegments = generateSelectionSetSegments(path);
for (const [fieldName, fieldData] of fieldDataByName) {
unresolvableFieldDatas.push({
externalSubgraphNames: fieldData.externalSubgraphNames,
fieldName,
selectionSet: renderSelectionSet(selectionSetSegments, fieldData),
subgraphNames: fieldData.subgraphNames,
Expand Down Expand Up @@ -247,6 +276,7 @@ export function generateEntityResolvabilityErrors({
const selectionSetSegments = generateSelectionSetSegments(fullPath);
for (const [fieldName, fieldData] of fieldDataByName) {
unresolvableFieldDatas.push({
externalSubgraphNames: fieldData.externalSubgraphNames,
fieldName,
selectionSet: renderSelectionSet(selectionSetSegments, fieldData),
subgraphNames: fieldData.subgraphNames,
Expand Down Expand Up @@ -289,6 +319,7 @@ export function generateSharedEntityResolvabilityErrors({
const selectionSetSegments = generateSelectionSetSegments(fullPath);
for (const [fieldName, fieldData] of fieldDataByName) {
unresolvableFieldDatas.push({
externalSubgraphNames: fieldData.externalSubgraphNames,
fieldName,
selectionSet: renderSelectionSet(selectionSetSegments, fieldData),
subgraphNames: fieldData.subgraphNames,
Expand Down
Loading
Loading