Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion apollo-router/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -567,7 +567,8 @@ pub(crate) enum SchemaError {
Api(String),

/// Connector error(s): {0}
Connector(String),
#[from(ignore)]
Connector(FederationError),
}

#[derive(Error, Display, Debug, PartialEq)]
Expand Down
37 changes: 2 additions & 35 deletions apollo-router/src/plugins/connectors/supergraph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -958,9 +958,7 @@ fn enum_values_for_graph(
#[cfg(test)]
mod tests {
use apollo_compiler::Schema;
use insta::assert_debug_snapshot;
use insta::assert_snapshot;
use itertools::Itertools;

use crate::plugins::connectors::Source;
use crate::spec::Schema as RouterSchema;
Expand All @@ -975,40 +973,9 @@ mod tests {
let inner = source.supergraph();

// new supergraph can be parsed into subgraphs
let result =
RouterSchema::parse(inner.serialize().to_string().as_str(), &Default::default())
.unwrap();
let _ = RouterSchema::parse(inner.serialize().to_string().as_str(), &Default::default())
.unwrap();

assert_snapshot!(inner.serialize().to_string());

assert_debug_snapshot!(
result
.subgraph_definition_and_names
.values()
.sorted()
.cloned()
.collect::<Vec<_>>(),
@r###"
[
"CONNECTOR_ENTITYACROSSBOTH_0",
"CONNECTOR_ENTITYACROSSBOTH_E_0",
"CONNECTOR_ENTITYACROSSBOTH_F_1",
"CONNECTOR_ENTITYINTERFACE_1",
"CONNECTOR_HELLO_2",
"CONNECTOR_HELLO_RELATED_3",
"CONNECTOR_HELLO_WORLD_2",
"CONNECTOR_MUTATION_MUTATION_4",
"CONNECTOR_QUERY_HELLOS_7",
"CONNECTOR_QUERY_HELLOWITHHEADERS_8",
"CONNECTOR_QUERY_HELLO_6",
"CONNECTOR_QUERY_INTERFACES_9",
"CONNECTOR_QUERY_UNIONS_10",
"CONNECTOR_QUERY_WITHARGUMENTS_5",
"CONNECTOR_TESTINGINTERFACEOBJECT_3",
"CONNECTOR_TESTINGINTERFACEOBJECT_D_11",
"CONNECTOR_TESTREQUIRES_SHIPPINGCOST_12",
]
"###
);
}
}
86 changes: 59 additions & 27 deletions apollo-router/src/spec/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,13 @@ use std::time::Instant;
use apollo_compiler::ast;
use apollo_compiler::schema::Implementers;
use apollo_compiler::validation::Valid;
use apollo_compiler::NodeStr;
use apollo_federation::error::FederationError;
use apollo_federation::sources::connect::expand::expand_connectors;
use apollo_federation::sources::connect::expand::ExpansionResult;
use apollo_federation::sources::connect::Connector;
use http::Uri;
use indexmap::IndexMap;
use semver::Version;
use semver::VersionReq;
use sha2::Digest;
Expand All @@ -31,12 +37,12 @@ pub(crate) struct Schema {
api_schema: Option<ApiSchema>,
pub(crate) schema_id: Arc<String>,

#[allow(dead_code)]
pub(crate) subgraph_definition_and_names: HashMap<String, String>,

/// If the schema contains connectors, we'll extract them and the inner
/// supergraph schema here for use in the router factory and query planner.
pub(crate) source: Option<Source>,

#[allow(dead_code)]
pub(crate) connectors_by_service_name: Option<IndexMap<NodeStr, Connector>>,
}

/// TODO: remove and use apollo_federation::Supergraph unconditionally
Expand Down Expand Up @@ -83,49 +89,72 @@ impl Schema {

pub(crate) fn parse(sdl: &str, config: &Configuration) -> Result<Self, SchemaError> {
let start = Instant::now();

let mut api_schema: Option<ApiSchema> = None;
let mut connectors_by_service_name: Option<IndexMap<NodeStr, Connector>> = None;
let expansion = expand_connectors(sdl).map_err(SchemaError::Connector)?;
let sdl = match expansion {
ExpansionResult::Expanded {
ref raw_sdl,
api_schema: api,
connectors_by_service_name: connectors,
} => {
api_schema = Some(ApiSchema(api));
connectors_by_service_name = Some(connectors);
raw_sdl
}
ExpansionResult::Unchanged => sdl,
};

let definitions = Self::parse_compiler_schema(sdl)?;

let mut subgraphs = HashMap::new();
let mut subgraph_definition_and_names = HashMap::new();

// TODO: error if not found?
if let Some(join_enum) = definitions.get_enum("join__Graph") {
for (subgraph_name, name, url) in join_enum.values.values().filter_map(|value| {
let subgraph_name = value.value.to_string();
for (name, url) in join_enum.values.values().filter_map(|value| {
let join_directive = value
.directives
.iter()
.find(|directive| directive.name.eq_ignore_ascii_case("join__graph"))?;
let name = join_directive.argument_by_name("name")?.as_str()?;
let url = join_directive.argument_by_name("url")?.as_str()?;
Some((subgraph_name, name, url))
Some((name, url))
}) {
if url.is_empty() {
return Err(SchemaError::MissingSubgraphUrl(name.to_string()));
}
#[cfg(unix)]
// there is no standard for unix socket URLs apparently
let url = if let Some(path) = url.strip_prefix("unix://") {
// there is no specified format for unix socket URLs (cf https://github.com/whatwg/url/issues/577)
// so a unix:// URL will not be parsed by http::Uri
// To fix that, hyperlocal came up with its own Uri type that can be converted to http::Uri.
// It hides the socket path in a hex encoded authority that the unix socket connector will
// know how to decode
hyperlocal::Uri::new(path, "/").into()
let is_connector = connectors_by_service_name
.as_ref()
.map(|connectors| connectors.contains_key(&NodeStr::from(name)))
.unwrap_or_default();

let url = if is_connector {
Uri::from_static("http://unused")
} else {
if url.is_empty() {
return Err(SchemaError::MissingSubgraphUrl(name.to_string()));
}
#[cfg(unix)]
// there is no standard for unix socket URLs apparently
if let Some(path) = url.strip_prefix("unix://") {
// there is no specified format for unix socket URLs (cf https://github.com/whatwg/url/issues/577)
// so a unix:// URL will not be parsed by http::Uri
// To fix that, hyperlocal came up with its own Uri type that can be converted to http::Uri.
// It hides the socket path in a hex encoded authority that the unix socket connector will
// know how to decode
hyperlocal::Uri::new(path, "/").into()
} else {
Uri::from_str(url)
.map_err(|err| SchemaError::UrlParse(name.to_string(), err))?
}
#[cfg(not(unix))]
Uri::from_str(url)
.map_err(|err| SchemaError::UrlParse(name.to_string(), err))?
};
#[cfg(not(unix))]
let url = Uri::from_str(url)
.map_err(|err| SchemaError::UrlParse(name.to_string(), err))?;

if subgraphs.insert(name.to_string(), url).is_some() {
return Err(SchemaError::Api(format!(
"must not have several subgraphs with same name '{name}'"
)));
}
subgraph_definition_and_names.insert(subgraph_name, name.to_string());
}
}

Expand All @@ -137,8 +166,11 @@ impl Schema {

let implementers_map = definitions.implementers_map();

let source = Source::new(&definitions)
.map_err(|e| SchemaError::Connector(format!("Failed to create connectors: {}", e)))?;
let source = Source::new(&definitions).map_err(|_| {
SchemaError::Connector(FederationError::internal(
"TODO remove when we remove Source",
))
})?;

let legacy_only = config.experimental_query_planner_mode == QueryPlannerMode::Legacy
&& config.experimental_api_schema_generation_mode == ApiSchemaMode::Legacy;
Expand All @@ -155,10 +187,10 @@ impl Schema {
supergraph,
subgraphs,
implementers_map,
api_schema: None,
api_schema,
schema_id,
subgraph_definition_and_names,
source,
connectors_by_service_name,
})
}

Expand Down