1#![warn(
17 rustdoc::broken_intra_doc_links,
18 unreachable_pub,
19 unreachable_patterns,
20 unused,
21 unused_qualifications,
22 dead_code,
23 while_true,
24 unconditional_panic,
25 clippy::all
26)]
27
28mod api_schema;
29mod compat;
30#[cfg(feature = "correctness")]
31pub mod correctness;
32mod display_helpers;
33pub mod error;
34pub mod link;
35pub mod merge;
36pub(crate) mod operation;
37pub mod query_graph;
38pub mod query_plan;
39pub mod schema;
40pub mod subgraph;
41pub(crate) mod supergraph;
42pub(crate) mod utils;
43
44use apollo_compiler::Schema;
45use apollo_compiler::ast::NamedType;
46use apollo_compiler::validation::Valid;
47use itertools::Itertools;
48use link::join_spec_definition::JOIN_VERSIONS;
49use schema::FederationSchema;
50
51pub use crate::api_schema::ApiSchemaOptions;
52use crate::error::FederationError;
53use crate::error::SingleFederationError;
54use crate::link::context_spec_definition::CONTEXT_VERSIONS;
55use crate::link::context_spec_definition::ContextSpecDefinition;
56use crate::link::join_spec_definition::JoinSpecDefinition;
57use crate::link::link_spec_definition::LinkSpecDefinition;
58use crate::link::spec::Identity;
59use crate::link::spec_definition::SpecDefinitions;
60use crate::merge::MergeFailure;
61use crate::merge::merge_subgraphs;
62use crate::schema::ValidFederationSchema;
63use crate::subgraph::ValidSubgraph;
64pub use crate::supergraph::ValidFederationSubgraph;
65pub use crate::supergraph::ValidFederationSubgraphs;
66
67pub(crate) type SupergraphSpecs = (
68 &'static LinkSpecDefinition,
69 &'static JoinSpecDefinition,
70 Option<&'static ContextSpecDefinition>,
71);
72
73pub(crate) fn validate_supergraph_for_query_planning(
74 supergraph_schema: &FederationSchema,
75) -> Result<SupergraphSpecs, FederationError> {
76 validate_supergraph(supergraph_schema, &JOIN_VERSIONS, &CONTEXT_VERSIONS)
77}
78
79pub(crate) fn validate_supergraph(
81 supergraph_schema: &FederationSchema,
82 join_versions: &'static SpecDefinitions<JoinSpecDefinition>,
83 context_versions: &'static SpecDefinitions<ContextSpecDefinition>,
84) -> Result<SupergraphSpecs, FederationError> {
85 let Some(metadata) = supergraph_schema.metadata() else {
86 return Err(SingleFederationError::InvalidFederationSupergraph {
87 message: "Invalid supergraph: must be a core schema".to_owned(),
88 }
89 .into());
90 };
91 let link_spec_definition = metadata.link_spec_definition()?;
92 let Some(join_link) = metadata.for_identity(&Identity::join_identity()) else {
93 return Err(SingleFederationError::InvalidFederationSupergraph {
94 message: "Invalid supergraph: must use the join spec".to_owned(),
95 }
96 .into());
97 };
98 let Some(join_spec_definition) = join_versions.find(&join_link.url.version) else {
99 return Err(SingleFederationError::InvalidFederationSupergraph {
100 message: format!(
101 "Invalid supergraph: uses unsupported join spec version {} (supported versions: {})",
102 join_link.url.version,
103 join_versions.versions().map(|v| v.to_string()).collect::<Vec<_>>().join(", "),
104 ),
105 }.into());
106 };
107 let context_spec_definition = metadata.for_identity(&Identity::context_identity()).map(|context_link| {
108 context_versions.find(&context_link.url.version).ok_or_else(|| {
109 SingleFederationError::InvalidFederationSupergraph {
110 message: format!(
111 "Invalid supergraph: uses unsupported context spec version {} (supported versions: {})",
112 context_link.url.version,
113 context_versions.versions().join(", "),
114 ),
115 }
116 })
117 }).transpose()?;
118 Ok((
119 link_spec_definition,
120 join_spec_definition,
121 context_spec_definition,
122 ))
123}
124
125pub struct Supergraph {
126 pub schema: ValidFederationSchema,
127}
128
129impl Supergraph {
130 pub fn new(schema_str: &str) -> Result<Self, FederationError> {
131 let schema = Schema::parse_and_validate(schema_str, "schema.graphql")?;
132 Self::from_schema(schema)
133 }
134
135 pub fn from_schema(schema: Valid<Schema>) -> Result<Self, FederationError> {
136 let schema = schema.into_inner();
137 let schema = FederationSchema::new(schema)?;
138
139 let _ = validate_supergraph_for_query_planning(&schema)?;
140
141 Ok(Self {
142 schema: schema.assume_valid()?,
144 })
145 }
146
147 pub fn compose(subgraphs: Vec<&ValidSubgraph>) -> Result<Self, MergeFailure> {
148 let schema = merge_subgraphs(subgraphs)?.schema;
149 Ok(Self {
150 schema: ValidFederationSchema::new(schema).map_err(Into::<MergeFailure>::into)?,
151 })
152 }
153
154 pub fn to_api_schema(
157 &self,
158 options: ApiSchemaOptions,
159 ) -> Result<ValidFederationSchema, FederationError> {
160 api_schema::to_api_schema(self.schema.clone(), options)
161 }
162
163 pub fn extract_subgraphs(&self) -> Result<ValidFederationSubgraphs, FederationError> {
164 supergraph::extract_subgraphs_from_supergraph(&self.schema, None)
165 }
166}
167
168const _: () = {
169 const fn assert_thread_safe<T: Sync + Send>() {}
170
171 assert_thread_safe::<Supergraph>();
172 assert_thread_safe::<query_plan::query_planner::QueryPlanner>();
173};
174
175pub(crate) fn is_leaf_type(schema: &Schema, ty: &NamedType) -> bool {
177 schema.get_scalar(ty).is_some() || schema.get_enum(ty).is_some()
178}