in controlplane/src/core/repositories/FederatedGraphRepository.ts [1503:1781]
parentLoop: for (const federatedGraph of federatedGraphs) {
// Get published subgraphs for recomposition of the federated graph
const subgraphs = await subgraphRepo.listByFederatedGraph({
federatedGraphTargetId: federatedGraph.targetId,
published: true,
});
const contracts = await contractRepo.bySourceFederatedGraphId(federatedGraph.id);
const tagOptionsByContractName = new Map<string, ContractTagOptions>();
for (const contract of contracts) {
tagOptionsByContractName.set(
contract.downstreamFederatedGraph.target.name,
newContractTagOptionsFromArrays(contract.excludeTags, contract.includeTags),
);
}
const baseCompositionSubgraphs = subgraphs.map((s) => ({
name: s.name,
url: s.routingUrl,
definitions: parse(s.schemaSDL),
}));
// Collects the base graph and applicable feature flag related graphs
const allSubgraphsToCompose: SubgraphsToCompose[] = await featureFlagRepo.getSubgraphsToCompose({
baseSubgraphs: subgraphs,
baseCompositionSubgraphs,
fedGraphLabelMatchers: federatedGraph.labelMatchers,
});
/* baseCompositionData contains the router execution config and the schema version ID for the source graph
* base composition (not a contract or feature flag composition)
* */
const baseCompositionData: BaseCompositionData = {
featureFlagRouterExecutionConfigByFeatureFlagName: new Map<string, FeatureFlagRouterExecutionConfig>(),
};
/* Map of the contract base composition schema version ID, router execution config,
* and any feature flag schema version IDs by contract ID */
const contractBaseCompositionDataByContractId = new Map<string, ContractBaseCompositionData>();
for (const subgraphsToCompose of allSubgraphsToCompose) {
const result: FederationResult | FederationResultWithContracts = getFederationResultWithPotentialContracts(
federatedGraph,
subgraphsToCompose,
tagOptionsByContractName,
);
if (!result.success) {
// Collect all composition errors
allCompositionErrors.push(
...result.errors.map((e) => ({
federatedGraphName: federatedGraph.name,
namespace: federatedGraph.namespace,
message: e.message,
featureFlag: subgraphsToCompose.featureFlagName || '',
})),
);
}
// Collect all composition warnings
allCompositionWarnings.push(
...result.warnings.map((w) => ({
federatedGraphName: federatedGraph.name,
namespace: federatedGraph.namespace,
message: w.message,
featureFlag: subgraphsToCompose.featureFlagName || '',
})),
);
if (!subgraphsToCompose.isFeatureFlagComposition && !result.success && !federatedGraph.contract) {
allCompositionErrors.push(unsuccessfulBaseCompositionError(federatedGraph.name, federatedGraph.namespace));
}
const composedGraph = mapResultToComposedGraph(federatedGraph, subgraphsToCompose.subgraphs, result);
const federatedSchemaVersionId = randomUUID();
// Build the router execution config if the composed schema is valid
const routerExecutionConfig = buildRouterExecutionConfig(
composedGraph,
federatedSchemaVersionId,
federatedGraph.routerCompatibilityVersion,
);
const baseComposition = await composer.saveComposition({
composedGraph,
composedById: actorId,
isFeatureFlagComposition: subgraphsToCompose.isFeatureFlagComposition,
federatedSchemaVersionId,
routerExecutionConfig,
featureFlagId: subgraphsToCompose.featureFlagId,
});
if (!result.success || !baseComposition.schemaVersionId || !routerExecutionConfig) {
/* If the base composition failed to compose or deploy, return to the parent loop, because
* contracts are not composed if the base composition fails.
*/
if (!subgraphsToCompose.isFeatureFlagComposition) {
continue parentLoop;
}
// Record the feature flag composition to upload (if there are no errors)
} else if (subgraphsToCompose.isFeatureFlagComposition) {
baseCompositionData.featureFlagRouterExecutionConfigByFeatureFlagName.set(
subgraphsToCompose.featureFlagName,
routerConfigToFeatureFlagExecutionConfig(routerExecutionConfig),
);
// Otherwise, this is the base composition, so store the schema version id
} else {
baseCompositionData.schemaVersionId = baseComposition.schemaVersionId;
baseCompositionData.routerExecutionConfig = routerExecutionConfig;
}
// If there are no contracts, there is nothing further to do
if (!('federationResultByContractName' in result)) {
continue;
}
for (const [contractName, contractResult] of result.federationResultByContractName) {
const contractGraph = await fedGraphRepo.byName(contractName, federatedGraph.namespace);
if (!contractGraph) {
throw new Error(`The contract graph "${contractName}" was not found.`);
}
if (!contractResult.success) {
allCompositionErrors.push(
...contractResult.errors.map((e) => ({
federatedGraphName: contractGraph.name,
namespace: contractGraph.namespace,
message: e.message,
featureFlag: subgraphsToCompose.featureFlagName,
})),
);
}
allCompositionWarnings.push(
...contractResult.warnings.map((w) => ({
federatedGraphName: contractGraph.name,
namespace: contractGraph.namespace,
message: w.message,
featureFlag: subgraphsToCompose.featureFlagName,
})),
);
const composedContract = mapResultToComposedGraph(
contractGraph,
subgraphsToCompose.subgraphs,
contractResult,
);
const contractSchemaVersionId = randomUUID();
// Build the router execution config if the composed schema is valid
const contractRouterExecutionConfig = buildRouterExecutionConfig(
composedContract,
contractSchemaVersionId,
federatedGraph.routerCompatibilityVersion,
);
const contractComposition = await composer.saveComposition({
composedGraph: composedContract,
composedById: actorId,
isFeatureFlagComposition: subgraphsToCompose.isFeatureFlagComposition,
federatedSchemaVersionId: contractSchemaVersionId,
routerExecutionConfig: contractRouterExecutionConfig,
featureFlagId: subgraphsToCompose.featureFlagId,
});
if (!contractResult.success || !contractComposition.schemaVersionId || !contractRouterExecutionConfig) {
continue;
}
/* If the base composition for which this contract has been made is NOT a feature flag composition,
* it must be the contract base composition, which must always be uploaded.
* The base composition is always the first item in the subgraphsToCompose array.
* */
if (!subgraphsToCompose.isFeatureFlagComposition) {
contractBaseCompositionDataByContractId.set(contractGraph.id, {
schemaVersionId: contractComposition.schemaVersionId,
routerExecutionConfig: contractRouterExecutionConfig,
featureFlagRouterExecutionConfigByFeatureFlagName: new Map<string, FeatureFlagRouterExecutionConfig>(),
});
continue;
}
/* If the contract has a feature flag, get the current array feature flag versions (or set a new one),
* and then push the current schema version to the array
* */
const existingContractBaseCompositionData = contractBaseCompositionDataByContractId.get(contractGraph.id);
/* If the existingContractSchemaVersions is undefined, it means the contract base composition failed.
* In this case, simply continue, because when iterating a feature flag for the source graph composition,
* there may not be any errors for the feature flag.
* */
if (!existingContractBaseCompositionData) {
continue;
}
existingContractBaseCompositionData.featureFlagRouterExecutionConfigByFeatureFlagName.set(
subgraphsToCompose.featureFlagName,
routerConfigToFeatureFlagExecutionConfig(contractRouterExecutionConfig),
);
}
}
const federatedGraphDTO = await this.byId(federatedGraph.id);
if (!federatedGraphDTO) {
throw new Error(`Fatal:The federated graph "${federatedGraph.name}" was not found.`);
}
if (!baseCompositionData.routerExecutionConfig) {
throw new Error(
`Fatal: The latest router execution config for federated graph "${federatedGraph.name}" was not generated.`,
);
}
if (!baseCompositionData.schemaVersionId) {
throw new Error(
`Fatal: The latest base composition for federated graph "${federatedGraph.name}" was not found.`,
);
}
const { errors: uploadErrors } = await composer.composeAndUploadRouterConfig({
federatedGraphId: federatedGraphDTO.id,
featureFlagRouterExecutionConfigByFeatureFlagName:
baseCompositionData.featureFlagRouterExecutionConfigByFeatureFlagName,
blobStorage,
organizationId: this.organizationId,
admissionConfig: {
cdnBaseUrl: admissionConfig.cdnBaseUrl,
jwtSecret: admissionConfig.webhookJWTSecret,
},
baseCompositionRouterExecutionConfig: baseCompositionData.routerExecutionConfig,
baseCompositionSchemaVersionId: baseCompositionData.schemaVersionId,
federatedGraphAdmissionWebhookURL: federatedGraphDTO.admissionWebhookURL,
federatedGraphAdmissionWebhookSecret: federatedGraphDTO.admissionWebhookSecret,
actorId,
});
allDeploymentErrors.push(
...uploadErrors
.filter((e) => e instanceof AdmissionError || e instanceof RouterConfigUploadError)
.map((e) => ({
federatedGraphName: federatedGraph.name,
namespace: federatedGraph.namespace,
message: e.message ?? '',
})),
);
for (const [
contractId,
{ featureFlagRouterExecutionConfigByFeatureFlagName, schemaVersionId, routerExecutionConfig },
] of contractBaseCompositionDataByContractId) {
const contractDTO = await this.byId(contractId);
if (!contractDTO) {
throw new Error(`Unexpected: Contract graph with id "${contractId}" not found after latest composition`);
}
const { errors: uploadErrors } = await composer.composeAndUploadRouterConfig({
admissionConfig: {
cdnBaseUrl: admissionConfig.cdnBaseUrl,
jwtSecret: admissionConfig.webhookJWTSecret,
},
baseCompositionRouterExecutionConfig: routerExecutionConfig,
baseCompositionSchemaVersionId: schemaVersionId,
blobStorage,
featureFlagRouterExecutionConfigByFeatureFlagName,
federatedGraphId: contractDTO.id,
organizationId: this.organizationId,
federatedGraphAdmissionWebhookURL: contractDTO.admissionWebhookURL,
federatedGraphAdmissionWebhookSecret: contractDTO.admissionWebhookSecret,
actorId,
});
allDeploymentErrors.push(
...uploadErrors
.filter((e) => e instanceof AdmissionError || e instanceof RouterConfigUploadError)
.map((e) => ({
federatedGraphName: federatedGraph.name,
namespace: federatedGraph.namespace,
message: e.message ?? '',
})),
);
}
}