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
81 changes: 41 additions & 40 deletions composition-go/index.global.js

Large diffs are not rendered by default.

9 changes: 5 additions & 4 deletions composition/src/errors/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -847,8 +847,9 @@ export function undefinedEntityInterfaceImplementationsError(
entityInterfaceFederationDataByTypeName: Map<string, EntityInterfaceFederationData>,
): Error {
let message =
`Federation was unsuccessful because any one subgraph that defines a specific entity interface` +
` must also define each and every entity object that implements that entity interface.\n`;
`Federation was unsuccessful because any one subgraph that defines a specific entity Interface` +
` must also define each and every entity Object that implements that entity Interface.\n` +
`Each entity Object must also explicitly define its implementation of the entity Interface.\n`;
for (const [typeName, undefinedImplementations] of invalidEntityInterfacesByTypeName) {
const entityInterfaceDatas = getOrThrowError(
entityInterfaceFederationDataByTypeName,
Expand All @@ -864,8 +865,8 @@ export function undefinedEntityInterfaceImplementationsError(
`"\n` +
` However, the definition of at least one of these implementations is missing in a subgraph that` +
` defines the entity interface "${typeName}":\n`;
for (const { subgraphName, concreteTypeNames } of undefinedImplementations) {
const disparities = getEntriesNotInHashSet(implementedConcreteTypeNames, concreteTypeNames);
for (const { subgraphName, definedConcreteTypeNames } of undefinedImplementations) {
const disparities = getEntriesNotInHashSet(implementedConcreteTypeNames, definedConcreteTypeNames);
message +=
` Subgraph "${subgraphName}" does not define the following implementations: "` +
disparities.join(QUOTATION_JOIN) +
Expand Down
3 changes: 2 additions & 1 deletion composition/src/utils/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,9 @@ export type InvalidRequiredInputValueData = {
};

export type InvalidEntityInterface = {
definedConcreteTypeNames: Set<string>;
requiredConcreteTypeNames: Set<string>;
subgraphName: string;
concreteTypeNames: Set<string>;
};

export type InputNodeKind = Kind.ENUM_TYPE_DEFINITION | Kind.INPUT_OBJECT_TYPE_DEFINITION | Kind.SCALAR_TYPE_DEFINITION;
Expand Down
47 changes: 43 additions & 4 deletions composition/src/v1/federation/federation-factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ import {
InvalidRequiredInputValueData,
} from '../../utils/types';
import { FederateSubgraphsContractV1Params, FederateSubgraphsWithContractsV1Params, FederationParams } from './types';
import { ContractName, FieldCoords, TypeName } from '../../types/types';
import { ContractName, FieldCoords, SubgraphName, TypeName } from '../../types/types';

export class FederationFactory {
authorizationDataByParentTypeName: Map<string, AuthorizationData>;
Expand Down Expand Up @@ -1617,7 +1617,7 @@ export class FederationFactory {
if (!isObjectDefinitionData(concreteTypeData)) {
continue;
}
// The subgraph locations of the interface object must be added to the concrete types that implement it
// The subgraph locations of the Interface Object must be added to the concrete types that implement it
const entityData = getOrThrowError(this.entityDataByTypeName, concreteTypeName, 'entityDataByTypeName');
entityData.subgraphNames.add(subgraphName);
const configurationData = configurationDataByTypeName.get(concreteTypeName);
Expand Down Expand Up @@ -1673,10 +1673,16 @@ export class FederationFactory {
}
}
const existingFieldData = concreteTypeData.fieldDataByName.get(fieldName);
// @shareable and @external need to be propagated (e.g., to satisfy interfaces)
if (existingFieldData) {
const isShareable = fieldData.isShareableBySubgraphName.get(subgraphName) ?? false;
existingFieldData.isShareableBySubgraphName.set(subgraphName, isShareable);
existingFieldData.subgraphNames.add(subgraphName);
const externalData = fieldData.externalFieldDataBySubgraphName.get(subgraphName);
if (!externalData) {
continue;
}
existingFieldData.externalFieldDataBySubgraphName.set(subgraphName, { ...externalData });
continue;
}
const isInaccessible =
Expand Down Expand Up @@ -3061,18 +3067,31 @@ function initializeFederationFactory({
}
}
const entityInterfaceErrors = new Array<Error>();
const definedConcreteTypeNamesBySubgraphName = new Map<SubgraphName, Set<TypeName>>();
for (const [typeName, entityInterfaceData] of entityInterfaceFederationDataByTypeName) {
const implementations = entityInterfaceData.concreteTypeNames.size;
for (const [subgraphName, subgraphData] of entityInterfaceData.subgraphDataByTypeName) {
const definedConcreteTypeNames = getValueOrDefault(
definedConcreteTypeNamesBySubgraphName,
subgraphName,
() => new Set<TypeName>(),
);
addIterableValuesToSet(subgraphData.concreteTypeNames, definedConcreteTypeNames);
if (!subgraphData.isInterfaceObject) {
if (subgraphData.resolvable && subgraphData.concreteTypeNames.size !== implementations) {
getValueOrDefault(invalidEntityInterfacesByTypeName, typeName, () => []).push({
getValueOrDefault(
invalidEntityInterfacesByTypeName,
typeName,
() => new Array<InvalidEntityInterface>(),
).push({
subgraphName,
concreteTypeNames: subgraphData.concreteTypeNames,
definedConcreteTypeNames: new Set<TypeName>(subgraphData.concreteTypeNames),
requiredConcreteTypeNames: new Set<TypeName>(entityInterfaceData.concreteTypeNames),
});
}
continue;
}
addIterableValuesToSet(entityInterfaceData.concreteTypeNames, definedConcreteTypeNames);
const { parentDefinitionDataByTypeName } = getOrThrowError(
result.internalSubgraphBySubgraphName,
subgraphName,
Expand All @@ -3091,6 +3110,26 @@ function initializeFederationFactory({
}
}
}
for (const [typeName, invalidInterfaces] of invalidEntityInterfacesByTypeName) {
const checkedInvalidInterfaces = new Array<InvalidEntityInterface>();
for (const invalidInterface of invalidInterfaces) {
const validTypeNames = definedConcreteTypeNamesBySubgraphName.get(invalidInterface.subgraphName);
if (!validTypeNames) {
checkedInvalidInterfaces.push(invalidInterface);
continue;
}
const definedTypeNames = invalidInterface.requiredConcreteTypeNames.intersection(validTypeNames);
if (invalidInterface.requiredConcreteTypeNames.size !== definedTypeNames.size) {
invalidInterface.definedConcreteTypeNames = definedTypeNames;
checkedInvalidInterfaces.push(invalidInterface);
}
}
if (checkedInvalidInterfaces.length > 0) {
invalidEntityInterfacesByTypeName.set(typeName, checkedInvalidInterfaces);
continue;
}
invalidEntityInterfacesByTypeName.delete(typeName);
}
if (invalidEntityInterfacesByTypeName.size > 0) {
Comment thread
Aenimus marked this conversation as resolved.
entityInterfaceErrors.push(
undefinedEntityInterfaceImplementationsError(
Expand Down
Loading
Loading