in composition/src/v1/federation/federation-factory.ts [1133:1407]
name: stringToNameNode(sourceData.name),
},
memberByMemberTypeName: new Map(sourceData.memberByMemberTypeName),
};
}
}
}
getParentTargetData({
existingData,
incomingData,
}: {
existingData?: ParentDefinitionData;
incomingData: ParentDefinitionData;
}): ParentDefinitionData {
if (!existingData) {
const targetData = this.copyParentDefinitionData(incomingData);
if (isParentDataRootType(targetData)) {
targetData.extensionType = ExtensionType.NONE;
}
return targetData;
}
extractPersistedDirectives(
existingData.persistedDirectivesData,
incomingData.directivesByDirectiveName,
this.persistedDirectiveDefinitionByDirectiveName,
);
return existingData;
}
upsertParentDefinitionData(incomingData: ParentDefinitionData, subgraphName: string) {
const entityInterfaceData = this.entityInterfaceFederationDataByTypeName.get(incomingData.name);
const existingData = this.parentDefinitionDataByTypeName.get(incomingData.name);
const targetData = this.getParentTargetData({ existingData, incomingData });
this.recordTagNamesByCoords(targetData);
const isParentInaccessible = isNodeDataInaccessible(targetData);
if (isParentInaccessible) {
this.inaccessibleCoords.add(targetData.name);
}
if (entityInterfaceData && entityInterfaceData.interfaceObjectSubgraphs.has(subgraphName)) {
targetData.kind = Kind.INTERFACE_TYPE_DEFINITION;
targetData.node.kind = Kind.INTERFACE_TYPE_DEFINITION;
}
if (!existingData) {
this.parentDefinitionDataByTypeName.set(targetData.name, targetData);
return;
}
if (targetData.kind !== incomingData.kind) {
if (
!entityInterfaceData ||
!entityInterfaceData.interfaceObjectSubgraphs.has(subgraphName) ||
targetData.kind !== Kind.INTERFACE_TYPE_DEFINITION ||
incomingData.kind !== Kind.OBJECT_TYPE_DEFINITION
) {
this.errors.push(
incompatibleParentKindMergeError(
targetData.name,
kindToTypeString(targetData.kind),
kindToTypeString(incomingData.kind),
),
);
return;
}
}
extractPersistedDirectives(
targetData.persistedDirectivesData,
incomingData.directivesByDirectiveName,
this.persistedDirectiveDefinitionByDirectiveName,
);
this.recordTagNamesByCoords(targetData);
addNewObjectValueMapEntries(
incomingData.configureDescriptionDataBySubgraphName,
targetData.configureDescriptionDataBySubgraphName,
);
setLongestDescription(targetData, incomingData);
setParentDataExtensionType(targetData, incomingData);
switch (targetData.kind) {
case Kind.ENUM_TYPE_DEFINITION:
if (!areKindsEqual(targetData, incomingData)) {
return;
}
targetData.appearances += 1;
targetData.isInaccessible ||= isParentInaccessible;
addIterableValuesToSet(incomingData.subgraphNames, targetData.subgraphNames);
for (const data of incomingData.enumValueDataByValueName.values()) {
this.upsertEnumValueData(targetData.enumValueDataByValueName, data, isParentInaccessible);
}
return;
case Kind.INPUT_OBJECT_TYPE_DEFINITION:
if (!areKindsEqual(targetData, incomingData)) {
return;
}
// targetData.isInaccessible currently yields the previous state not the new one.
if (isParentInaccessible && !targetData.isInaccessible) {
this.propagateInaccessibilityToExistingChildren(targetData);
}
targetData.isInaccessible ||= isParentInaccessible;
addIterableValuesToSet(incomingData.subgraphNames, targetData.subgraphNames);
for (const inputValueData of incomingData.inputValueDataByValueName.values()) {
this.upsertInputValueData(
targetData.inputValueDataByValueName,
inputValueData,
targetData.name,
targetData.isInaccessible,
);
}
return;
case Kind.INTERFACE_TYPE_DEFINITION:
// intentional fallthrough
case Kind.OBJECT_TYPE_DEFINITION:
// Not a type guard due to entity interfaces
const compositeOutputData = incomingData as CompositeOutputData;
// targetData.isInaccessible is not yet updated with the newest state
if (isParentInaccessible && !targetData.isInaccessible) {
this.propagateInaccessibilityToExistingChildren(targetData);
}
targetData.isInaccessible ||= isParentInaccessible;
addIterableValuesToSet(
compositeOutputData.implementedInterfaceTypeNames,
targetData.implementedInterfaceTypeNames,
);
addIterableValuesToSet(compositeOutputData.subgraphNames, targetData.subgraphNames);
for (const fieldData of compositeOutputData.fieldDataByFieldName.values()) {
this.upsertFieldData(targetData.fieldDataByFieldName, fieldData, targetData.isInaccessible);
}
return;
case Kind.UNION_TYPE_DEFINITION:
if (!areKindsEqual(targetData, incomingData)) {
return;
}
addMapEntries(incomingData.memberByMemberTypeName, targetData.memberByMemberTypeName);
return;
default:
// Scalar
return;
}
}
propagateInaccessibilityToExistingChildren(
data: InputObjectDefinitionData | InterfaceDefinitionData | ObjectDefinitionData,
) {
switch (data.kind) {
case Kind.INPUT_OBJECT_TYPE_DEFINITION:
for (const inputFieldData of data.inputValueDataByValueName.values()) {
this.inaccessibleCoords.add(inputFieldData.federatedCoords);
}
break;
default:
for (const fieldData of data.fieldDataByFieldName.values()) {
this.inaccessibleCoords.add(fieldData.federatedCoords);
for (const inputValueData of fieldData.argumentDataByArgumentName.values()) {
this.inaccessibleCoords.add(inputValueData.federatedCoords);
}
}
}
}
upsertPersistedDirectiveDefinitionData(incomingData: PersistedDirectiveDefinitionData, subgraphNumber: number) {
const name = incomingData.name;
const existingData = this.potentialPersistedDirectiveDefinitionDataByDirectiveName.get(name);
if (!existingData) {
// The executable directive must be defined in all subgraphs to be persisted.
if (subgraphNumber > 1) {
return;
}
const argumentDataByArgumentName = new Map<string, InputValueData>();
for (const inputValueData of incomingData.argumentDataByArgumentName.values()) {
this.namedInputValueTypeNames.add(inputValueData.namedTypeName);
this.upsertInputValueData(argumentDataByArgumentName, inputValueData, `@${incomingData.name}`, false);
}
this.potentialPersistedDirectiveDefinitionDataByDirectiveName.set(name, {
argumentDataByArgumentName,
executableLocations: new Set<string>(incomingData.executableLocations),
name,
repeatable: incomingData.repeatable,
subgraphNames: new Set<string>(incomingData.subgraphNames),
description: incomingData.description,
});
return;
}
// If the executable directive has not been defined in at least one graph, the definition should not be persisted
if (existingData.subgraphNames.size + 1 !== subgraphNumber) {
this.potentialPersistedDirectiveDefinitionDataByDirectiveName.delete(name);
return;
}
setMutualExecutableLocations(existingData, incomingData.executableLocations);
// If there are no mutually defined executable locations, the definition should not be persisted
if (existingData.executableLocations.size < 1) {
this.potentialPersistedDirectiveDefinitionDataByDirectiveName.delete(name);
return;
}
for (const inputValueData of incomingData.argumentDataByArgumentName.values()) {
this.namedInputValueTypeNames.add(getTypeNodeNamedTypeName(inputValueData.type));
this.upsertInputValueData(
existingData.argumentDataByArgumentName,
inputValueData,
`@${existingData.name}`,
false,
);
}
setLongestDescription(existingData, incomingData);
existingData.repeatable &&= incomingData.repeatable;
addIterableValuesToSet(incomingData.subgraphNames, existingData.subgraphNames);
}
shouldUpdateFederatedFieldAbstractNamedType(abstractTypeName: string, objectTypeNames: Set<string>): boolean {
if (!abstractTypeName) {
return false;
}
const concreteTypeNames = this.concreteTypeNamesByAbstractTypeName.get(abstractTypeName);
if (!concreteTypeNames || concreteTypeNames.size < 1) {
return false;
}
for (const objectTypeName of objectTypeNames) {
if (!concreteTypeNames.has(objectTypeName)) {
return false;
}
}
return true;
}
updateTypeNodeNamedType(typeNode: MutableTypeNode, namedTypeName: string) {
let lastTypeNode = typeNode;
for (let i = 0; i < MAXIMUM_TYPE_NESTING; i++) {
if (lastTypeNode.kind === Kind.NAMED_TYPE) {
lastTypeNode.name = stringToNameNode(namedTypeName);
return;
}
lastTypeNode = lastTypeNode.type;
}
}
handleDisparateFieldNamedTypes() {
for (const [fieldCoordinates, subgraphNamesByNamedTypeName] of this.subgraphNamesByNamedTypeNameByFieldCoords) {
const coordinates = fieldCoordinates.split(PERIOD);
if (coordinates.length !== 2) {
continue;
}
const compositeOutputData = this.parentDefinitionDataByTypeName.get(coordinates[0]);
if (!compositeOutputData) {
this.errors.push(undefinedTypeError(coordinates[0]));
continue;
}
// This error should never happen
if (
compositeOutputData.kind !== Kind.INTERFACE_TYPE_DEFINITION &&
compositeOutputData.kind !== Kind.OBJECT_TYPE_DEFINITION
) {
this.errors.push(
unexpectedNonCompositeOutputTypeError(coordinates[0], kindToTypeString(compositeOutputData.kind)),
);
continue;
}
const fieldData = compositeOutputData.fieldDataByFieldName.get(coordinates[1]);
// This error should never happen
if (!fieldData) {
this.errors.push(unknownFieldDataError(fieldCoordinates));
continue;
}
const interfaceDataByTypeName = new Map<string, InterfaceDefinitionData>();
const objectTypeNames = new Set<string>();
let unionTypeName = '';
for (const namedTypeName of subgraphNamesByNamedTypeName.keys()) {
if (BASE_SCALARS.has(namedTypeName)) {
this.errors.push(incompatibleFederatedFieldNamedTypeError(fieldCoordinates, subgraphNamesByNamedTypeName));
break;
}
const namedTypeData = this.parentDefinitionDataByTypeName.get(namedTypeName);
// This error should never happen
if (!namedTypeData) {
this.errors.push(unknownNamedTypeError(fieldCoordinates, namedTypeName));
break;
}
switch (namedTypeData.kind) {
case Kind.INTERFACE_TYPE_DEFINITION: {