Skip to main content

apollo_federation/composition/
mod.rs

1mod satisfiability;
2
3use std::vec;
4
5use tracing::instrument;
6
7pub use crate::composition::satisfiability::validate_satisfiability;
8use crate::error::CompositionError;
9use crate::merger::merge::Merger;
10pub use crate::schema::schema_upgrader::upgrade_subgraphs_if_necessary;
11use crate::schema::validators::root_fields::validate_consistent_root_fields;
12use crate::subgraph::typestate::Expanded;
13use crate::subgraph::typestate::Initial;
14use crate::subgraph::typestate::Subgraph;
15use crate::subgraph::typestate::Validated;
16pub use crate::supergraph::Merged;
17pub use crate::supergraph::Satisfiable;
18pub use crate::supergraph::Supergraph;
19
20#[instrument(skip(subgraphs))]
21pub fn compose(
22    subgraphs: Vec<Subgraph<Initial>>,
23) -> Result<Supergraph<Satisfiable>, Vec<CompositionError>> {
24    tracing::debug!("Expanding subgraphs...");
25    let expanded_subgraphs = expand_subgraphs(subgraphs)?;
26    tracing::debug!("Upgrading subgraphs...");
27    let validated_subgraphs = upgrade_subgraphs_if_necessary(expanded_subgraphs)?;
28
29    tracing::debug!("Pre-merge validations...");
30    pre_merge_validations(&validated_subgraphs)?;
31    tracing::debug!("Merging subgraphs...");
32    let supergraph = merge_subgraphs(validated_subgraphs)?;
33    tracing::debug!("Post-merge validations...");
34    post_merge_validations(&supergraph)?;
35    tracing::debug!("Validating satisfiability...");
36    validate_satisfiability(supergraph)
37}
38
39/// Apollo Federation allow subgraphs to specify partial schemas (i.e. "import" directives through
40/// `@link`). This function will update subgraph schemas with all missing federation definitions.
41#[instrument(skip(subgraphs))]
42pub fn expand_subgraphs(
43    subgraphs: Vec<Subgraph<Initial>>,
44) -> Result<Vec<Subgraph<Expanded>>, Vec<CompositionError>> {
45    let mut errors: Vec<CompositionError> = vec![];
46    let expanded: Vec<Subgraph<Expanded>> = subgraphs
47        .into_iter()
48        .map(|s| s.expand_links())
49        .filter_map(|r| r.map_err(|e| errors.extend(e.to_composition_errors())).ok())
50        .collect();
51    if errors.is_empty() {
52        Ok(expanded)
53    } else {
54        Err(errors)
55    }
56}
57
58/// Perform validations that require information about all available subgraphs.
59#[instrument(skip(subgraphs))]
60pub fn pre_merge_validations(
61    subgraphs: &[Subgraph<Validated>],
62) -> Result<(), Vec<CompositionError>> {
63    validate_consistent_root_fields(subgraphs)?;
64    // TODO: (FED-713) Implement any pre-merge validations that require knowledge of all subgraphs.
65    Ok(())
66}
67
68#[instrument(skip(subgraphs))]
69pub fn merge_subgraphs(
70    subgraphs: Vec<Subgraph<Validated>>,
71) -> Result<Supergraph<Merged>, Vec<CompositionError>> {
72    let merger = Merger::new(subgraphs, Default::default()).map_err(|e| {
73        vec![CompositionError::InternalError {
74            message: e.to_string(),
75        }]
76    })?;
77    let result = merger.merge().map_err(|e| {
78        vec![CompositionError::InternalError {
79            message: e.to_string(),
80        }]
81    })?;
82    tracing::trace!(
83        "Merge has {} errors and {} hints",
84        result.errors.len(),
85        result.hints.len()
86    );
87    if result.errors.is_empty() {
88        let Some(supergraph_schema) = result.supergraph else {
89            return Err(vec![CompositionError::InternalError {
90                message: "Merge completed with no supergraph schema".to_string(),
91            }]);
92        };
93        let supergraph = Supergraph::with_hints(supergraph_schema, result.hints);
94        Ok(supergraph)
95    } else {
96        Err(result.errors)
97    }
98}
99
100#[instrument(skip(_supergraph))]
101pub fn post_merge_validations(
102    _supergraph: &Supergraph<Merged>,
103) -> Result<(), Vec<CompositionError>> {
104    // TODO: (FED-714) Implement any post-merge validations other than satisfiability, which is
105    // checked separately.
106    Ok(())
107}