in composition/src/v1/normalization/normalization-factory.ts [3573:3760]
export function batchNormalize(subgraphs: Subgraph[]): BatchNormalizationResult {
const authorizationDataByParentTypeName = new Map<string, AuthorizationData>();
const concreteTypeNamesByAbstractTypeName = new Map<string, Set<string>>();
const entityDataByTypeName = new Map<string, EntityData>();
const internalSubgraphBySubgraphName = new Map<string, InternalSubgraph>();
const allOverridesByTargetSubgraphName = new Map<string, Map<string, Set<string>>>();
const overrideSourceSubgraphNamesByFieldPath = new Map<string, string[]>();
const duplicateOverriddenFieldPaths = new Set<string>();
const parentDefinitionDataMapsBySubgraphName = new Map<string, Map<string, ParentDefinitionData>>();
const subgraphNames = new Set<string>();
const nonUniqueSubgraphNames = new Set<string>();
const invalidNameErrorMessages: string[] = [];
const invalidOrScopesHostPaths = new Set<string>();
const warnings: Array<Warning> = [];
const validationErrors: Array<Error> = [];
// Record the subgraph names first, so that subgraph references can be validated
for (const subgraph of subgraphs) {
if (subgraph.name) {
recordSubgraphName(subgraph.name, subgraphNames, nonUniqueSubgraphNames);
}
}
const internalGraph = new Graph();
for (let i = 0; i < subgraphs.length; i++) {
const subgraph = subgraphs[i];
const subgraphName = subgraph.name || `subgraph-${i}-${Date.now()}`;
if (!subgraph.name) {
invalidNameErrorMessages.push(invalidSubgraphNameErrorMessage(i, subgraphName));
}
const normalizationResult = normalizeSubgraph(subgraph.definitions, subgraph.name, internalGraph);
if (normalizationResult.warnings.length > 0) {
warnings.push(...normalizationResult.warnings);
}
if (!normalizationResult.success) {
validationErrors.push(subgraphValidationError(subgraphName, normalizationResult.errors));
continue;
}
if (!normalizationResult) {
validationErrors.push(subgraphValidationError(subgraphName, [subgraphValidationFailureError]));
continue;
}
parentDefinitionDataMapsBySubgraphName.set(subgraphName, normalizationResult.parentDefinitionDataByTypeName);
for (const authorizationData of normalizationResult.authorizationDataByParentTypeName.values()) {
upsertAuthorizationData(authorizationDataByParentTypeName, authorizationData, invalidOrScopesHostPaths);
}
for (const [
abstractTypeName,
incomingConcreteTypeNames,
] of normalizationResult.concreteTypeNamesByAbstractTypeName) {
const existingConcreteTypeNames = concreteTypeNamesByAbstractTypeName.get(abstractTypeName);
if (!existingConcreteTypeNames) {
concreteTypeNamesByAbstractTypeName.set(abstractTypeName, new Set<string>(incomingConcreteTypeNames));
continue;
}
addIterableValuesToSet(incomingConcreteTypeNames, existingConcreteTypeNames);
}
for (const [typeName, entityData] of normalizationResult.entityDataByTypeName) {
const keyFieldSetDataByFieldSet = entityData.keyFieldSetDatasBySubgraphName.get(subgraphName);
if (!keyFieldSetDataByFieldSet) {
continue;
}
upsertEntityData({
entityDataByTypeName,
keyFieldSetDataByFieldSet,
typeName,
subgraphName,
});
}
if (subgraph.name) {
internalSubgraphBySubgraphName.set(subgraphName, {
conditionalFieldDataByCoordinates: normalizationResult.conditionalFieldDataByCoordinates,
configurationDataByTypeName: normalizationResult.configurationDataByTypeName,
definitions: normalizationResult.subgraphAST,
entityInterfaces: normalizationResult.entityInterfaces,
isVersionTwo: normalizationResult.isVersionTwo,
keyFieldNamesByParentTypeName: normalizationResult.keyFieldNamesByParentTypeName,
name: subgraphName,
operationTypes: normalizationResult.operationTypes,
overriddenFieldNamesByParentTypeName: new Map<string, Set<string>>(),
parentDefinitionDataByTypeName: normalizationResult.parentDefinitionDataByTypeName,
persistedDirectiveDefinitionDataByDirectiveName:
normalizationResult.persistedDirectiveDefinitionDataByDirectiveName,
schema: normalizationResult.schema,
url: subgraph.url,
});
}
if (normalizationResult.overridesByTargetSubgraphName.size < 1) {
continue;
}
for (const [targetSubgraphName, overridesData] of normalizationResult.overridesByTargetSubgraphName) {
const isTargetValid = subgraphNames.has(targetSubgraphName);
for (const [parentTypeName, fieldNames] of overridesData) {
/* It's possible for a renamed root type to have a field overridden, so make sure any errors at this stage are
propagated with the original typename. */
const originalParentTypeName =
normalizationResult.originalTypeNameByRenamedTypeName.get(parentTypeName) || parentTypeName;
if (!isTargetValid) {
warnings.push(
invalidOverrideTargetSubgraphNameWarning(
targetSubgraphName,
originalParentTypeName,
[...fieldNames],
subgraph.name,
),
);
} else {
const overridesData = getValueOrDefault(
allOverridesByTargetSubgraphName,
targetSubgraphName,
() => new Map<string, Set<string>>(),
);
const existingFieldNames = getValueOrDefault(
overridesData,
parentTypeName,
() => new Set<string>(fieldNames),
);
addIterableValuesToSet(fieldNames, existingFieldNames);
}
for (const fieldName of fieldNames) {
const fieldCoords = `${originalParentTypeName}.${fieldName}`;
const sourceSubgraphs = overrideSourceSubgraphNamesByFieldPath.get(fieldCoords);
if (!sourceSubgraphs) {
overrideSourceSubgraphNamesByFieldPath.set(fieldCoords, [subgraphName]);
continue;
}
sourceSubgraphs.push(subgraphName);
duplicateOverriddenFieldPaths.add(fieldCoords);
}
}
}
}
const allErrors: Array<Error> = [];
if (invalidOrScopesHostPaths.size > 0) {
allErrors.push(orScopesLimitError(maxOrScopes, [...invalidOrScopesHostPaths]));
}
if (invalidNameErrorMessages.length > 0 || nonUniqueSubgraphNames.size > 0) {
allErrors.push(invalidSubgraphNamesError([...nonUniqueSubgraphNames], invalidNameErrorMessages));
}
if (duplicateOverriddenFieldPaths.size > 0) {
const duplicateOverriddenFieldErrorMessages: string[] = [];
for (const fieldPath of duplicateOverriddenFieldPaths) {
const sourceSubgraphNames = getOrThrowError(
overrideSourceSubgraphNamesByFieldPath,
fieldPath,
'overrideSourceSubgraphNamesByFieldPath',
);
duplicateOverriddenFieldErrorMessages.push(duplicateOverriddenFieldErrorMessage(fieldPath, sourceSubgraphNames));
}
allErrors.push(duplicateOverriddenFieldsError(duplicateOverriddenFieldErrorMessages));
}
allErrors.push(...validationErrors);
if (allErrors.length > 0) {
return {
errors: allErrors,
success: false,
warnings,
};
}
for (const [targetSubgraphName, overridesData] of allOverridesByTargetSubgraphName) {
const internalSubgraph = getOrThrowError(
internalSubgraphBySubgraphName,
targetSubgraphName,
'internalSubgraphBySubgraphName',
);
internalSubgraph.overriddenFieldNamesByParentTypeName = overridesData;
for (const [parentTypeName, fieldNames] of overridesData) {
const configurationData = internalSubgraph.configurationDataByTypeName.get(parentTypeName);
if (!configurationData) {
continue;
}
subtractSourceSetFromTargetSet(fieldNames, configurationData.fieldNames);
if (configurationData.fieldNames.size < 1) {
internalSubgraph.configurationDataByTypeName.delete(parentTypeName);
}
}
}
return {
authorizationDataByParentTypeName,
concreteTypeNamesByAbstractTypeName,
entityDataByTypeName,
internalSubgraphBySubgraphName: internalSubgraphBySubgraphName,
internalGraph,
success: true,
warnings,
};
}