Skip to content

Commit

Permalink
Update structure of composition::supergraph::config module (apollogra…
Browse files Browse the repository at this point in the history
…phql#2285)

Just moves some files around and tries to group them a bit more logically
  • Loading branch information
dotdat authored Dec 4, 2024
1 parent 8a83b38 commit d73595f
Show file tree
Hide file tree
Showing 24 changed files with 1,507 additions and 1,390 deletions.
8 changes: 4 additions & 4 deletions src/command/supergraph/compose/do_compose.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,11 @@ use crate::{
supergraph::{
binary::{OutputTarget, SupergraphBinary},
config::{
resolve::{
subgraph::FullyResolvedSubgraph, FullyResolvedSubgraphs,
FullyResolvedSupergraphConfig, UnresolvedSupergraphConfig,
full::{
FullyResolvedSubgraph, FullyResolvedSubgraphs, FullyResolvedSupergraphConfig,
},
SupergraphConfigResolver,
resolver::SupergraphConfigResolver,
unresolved::UnresolvedSupergraphConfig,
},
install::InstallSupergraph,
version::SupergraphVersion,
Expand Down
6 changes: 3 additions & 3 deletions src/composition/runner/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ use super::{
events::CompositionEvent,
supergraph::{
binary::{OutputTarget, SupergraphBinary},
config::resolve::{
subgraph::LazilyResolvedSubgraph, FullyResolvedSubgraphs,
LazilyResolvedSupergraphConfig,
config::{
full::FullyResolvedSubgraphs,
lazy::{LazilyResolvedSubgraph, LazilyResolvedSupergraphConfig},
},
},
watchers::{composition::CompositionWatcher, subgraphs::SubgraphWatchers},
Expand Down
4 changes: 4 additions & 0 deletions src/composition/supergraph/config/error/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
//! This module errors related to resolving SupergraphConfigs that are also shared across multiple submodules of [`rover::composition::supergraph::config`]
mod subgraph;
pub use subgraph::*;
54 changes: 54 additions & 0 deletions src/composition/supergraph/config/error/subgraph.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
use std::path::PathBuf;

use camino::Utf8PathBuf;

/// Errors that may occur as a result of resolving subgraphs
#[derive(thiserror::Error, Debug)]
pub enum ResolveSubgraphError {
/// Occurs when the subgraph schema file cannot found relative to the supplied
/// supergraph config file
#[error("Could not find schema file ({path}) relative to ({supergraph_config_path}) for subgraph `{subgraph_name}`")]
FileNotFound {
/// The subgraph name that failed to be resolved
subgraph_name: String,
/// Supplied path to the supergraph config file
supergraph_config_path: Utf8PathBuf,
/// Supplied path to the subgraph schema file
path: PathBuf,
/// The source error
source: std::io::Error,
},
/// Occurs as a result of an IO error
#[error(transparent)]
Io(#[from] std::io::Error),
/// Occurs as a result of a rover_std::Fs error
#[error(transparent)]
Fs(Box<dyn std::error::Error + Send + Sync>),
/// Occurs when a introspection against a subgraph fails
#[error("Failed to introspect the subgraph {subgraph_name}.")]
IntrospectionError {
/// The subgraph name that failed to be resolved
subgraph_name: String,
/// The source error
source: Box<dyn std::error::Error + Send + Sync>,
},
/// Occurs when a supplied graph ref cannot be parsed
#[error("Invalid graph ref: {graph_ref}")]
InvalidGraphRef {
/// The supplied graph ref
graph_ref: String,
/// The source error
source: Box<dyn std::error::Error + Send + Sync>,
},
/// Occurs when fetching a remote subgraph fails
#[error("Failed to fetch the sdl for subgraph `{subgraph_name}` from remote")]
FetchRemoteSdlError {
/// The name of the subgraph that failed to be resolved
subgraph_name: String,
/// The source error
source: Box<dyn std::error::Error + Send + Sync>,
},
/// Occurs when a supergraph config filepath waqs expected but not found
#[error("Failed to find the supergraph config, which is required when resolving schemas in a file relative to a supergraph config")]
SupergraphConfigMissing,
}
13 changes: 13 additions & 0 deletions src/composition/supergraph/config/full/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//! Provides objects related to fully resolving a supergraph config.
//!
//! Full resolution is the process of taking a subgraph config and producing
//! a SDL string from whatever subgraph source is provided. This is the input that
//! the supergraph binary expects.
mod subgraph;
mod subgraphs;
mod supergraph;

pub use subgraph::*;
pub use subgraphs::*;
pub use supergraph::*;
159 changes: 159 additions & 0 deletions src/composition/supergraph/config/full/subgraph.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
use std::str::FromStr;

use apollo_federation_types::config::{SchemaSource, SubgraphConfig};
use apollo_parser::{cst, Parser};
use buildstructor::buildstructor;
use camino::Utf8PathBuf;
use derive_getters::Getters;
use rover_client::shared::GraphRef;
use rover_std::Fs;

use crate::{
composition::supergraph::config::{
error::ResolveSubgraphError, unresolved::UnresolvedSubgraph,
},
utils::effect::{fetch_remote_subgraph::FetchRemoteSubgraph, introspect::IntrospectSubgraph},
};

/// Represents a [`SubgraphConfig`] that has been resolved down to an SDL
#[derive(Clone, Debug, Eq, PartialEq, Getters)]
pub struct FullyResolvedSubgraph {
#[getter(skip)]
routing_url: Option<String>,
#[getter(skip)]
schema: String,
is_fed_two: bool,
}

#[buildstructor]
impl FullyResolvedSubgraph {
/// Hook for [`buildstructor::buildstructor`]'s builder pattern to create a [`FullyResolvedSubgraph`]
#[builder]
pub fn new(
schema: String,
routing_url: Option<String>,
is_fed_two: Option<bool>,
) -> FullyResolvedSubgraph {
FullyResolvedSubgraph {
schema,
routing_url,
is_fed_two: is_fed_two.unwrap_or_default(),
}
}
/// Resolves a [`UnresolvedSubgraph`] to a [`FullyResolvedSubgraph`]
pub async fn resolve(
introspect_subgraph_impl: &impl IntrospectSubgraph,
fetch_remote_subgraph_impl: &impl FetchRemoteSubgraph,
supergraph_config_root: Option<&Utf8PathBuf>,
unresolved_subgraph: UnresolvedSubgraph,
) -> Result<FullyResolvedSubgraph, ResolveSubgraphError> {
match unresolved_subgraph.schema() {
SchemaSource::File { file } => {
let supergraph_config_root =
supergraph_config_root.ok_or(ResolveSubgraphError::SupergraphConfigMissing)?;
let file = unresolved_subgraph.resolve_file_path(supergraph_config_root, file)?;
let schema =
Fs::read_file(&file).map_err(|err| ResolveSubgraphError::Fs(Box::new(err)))?;
let is_fed_two = schema_contains_link_directive(&schema);
Ok(FullyResolvedSubgraph {
routing_url: unresolved_subgraph.routing_url().clone(),
schema,
is_fed_two,
})
}
SchemaSource::SubgraphIntrospection {
subgraph_url,
introspection_headers,
} => {
let schema = introspect_subgraph_impl
.introspect_subgraph(
subgraph_url.clone(),
introspection_headers.clone().unwrap_or_default(),
)
.await
.map_err(|err| ResolveSubgraphError::IntrospectionError {
subgraph_name: unresolved_subgraph.name().to_string(),
source: Box::new(err),
})?;
let routing_url = unresolved_subgraph
.routing_url()
.clone()
.or_else(|| Some(subgraph_url.to_string()));
let is_fed_two = schema_contains_link_directive(&schema);
Ok(FullyResolvedSubgraph {
routing_url,
schema,
is_fed_two,
})
}
SchemaSource::Subgraph {
graphref: graph_ref,
subgraph,
} => {
let graph_ref = GraphRef::from_str(graph_ref).map_err(|err| {
ResolveSubgraphError::InvalidGraphRef {
graph_ref: graph_ref.clone(),
source: Box::new(err),
}
})?;
let remote_subgraph = fetch_remote_subgraph_impl
.fetch_remote_subgraph(graph_ref, subgraph.to_string())
.await
.map_err(|err| ResolveSubgraphError::FetchRemoteSdlError {
subgraph_name: subgraph.to_string(),
source: Box::new(err),
})?;
let schema = remote_subgraph.schema().clone();
let is_fed_two = schema_contains_link_directive(&schema);
Ok(FullyResolvedSubgraph {
routing_url: unresolved_subgraph
.routing_url()
.clone()
.or(Some(remote_subgraph.routing_url().to_string())),
schema,
is_fed_two,
})
}
SchemaSource::Sdl { sdl } => {
let is_fed_two = schema_contains_link_directive(sdl);
Ok(FullyResolvedSubgraph {
routing_url: None,
schema: sdl.to_string(),
is_fed_two,
})
}
}
}
}

impl From<FullyResolvedSubgraph> for SubgraphConfig {
fn from(value: FullyResolvedSubgraph) -> Self {
SubgraphConfig {
routing_url: value.routing_url,
schema: SchemaSource::Sdl { sdl: value.schema },
}
}
}

fn schema_contains_link_directive(sdl: &str) -> bool {
let parser = Parser::new(sdl);
let parsed_ast = parser.parse();
let doc = parsed_ast.document();
doc.definitions().any(|definition| {
match definition {
cst::Definition::SchemaExtension(ext) => ext.directives(),
cst::Definition::SchemaDefinition(def) => def.directives(),
_ => None,
}
.map(|d| d.directives())
.map(|mut directives| {
directives.any(|directive| {
directive
.name()
.map(|name| "link" == name.text())
.unwrap_or_default()
})
})
.unwrap_or_default()
})
}
73 changes: 73 additions & 0 deletions src/composition/supergraph/config/full/subgraphs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
use std::collections::BTreeMap;

use apollo_federation_types::config::{SchemaSource, SubgraphConfig, SupergraphConfig};
use thiserror::Error;

/// Error that occurs when a subgraph schema source is invalid
#[derive(Error, Debug)]
#[error("Invalid schema source: {:?}", .schema_source)]
pub struct InvalidSchemaSource {
schema_source: SchemaSource,
}

/// Object that contains the completed set of subgraphs resolved to their SDLs
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct FullyResolvedSubgraphs {
subgraphs: BTreeMap<String, String>,
}

impl FullyResolvedSubgraphs {
#[cfg(test)]
pub fn new(subgraphs: BTreeMap<String, String>) -> FullyResolvedSubgraphs {
FullyResolvedSubgraphs { subgraphs }
}

/// Used to upsert a fully resolved subgraph into this object's definitions
pub fn upsert_subgraph(&mut self, name: String, schema: String) {
self.subgraphs.insert(name, schema);
}

/// Removes a subgraph from this object's definitions
pub fn remove_subgraph(&mut self, name: &str) {
self.subgraphs.remove(name);
}
}

impl TryFrom<SupergraphConfig> for FullyResolvedSubgraphs {
type Error = Vec<InvalidSchemaSource>;
fn try_from(value: SupergraphConfig) -> Result<Self, Self::Error> {
let mut errors = Vec::new();
let mut subgraph_sdls = BTreeMap::new();
for (name, subgraph_config) in value.into_iter() {
if let SchemaSource::Sdl { sdl } = subgraph_config.schema {
subgraph_sdls.insert(name, sdl);
} else {
errors.push(InvalidSchemaSource {
schema_source: subgraph_config.schema,
});
}
}
if errors.is_empty() {
Ok(FullyResolvedSubgraphs {
subgraphs: subgraph_sdls,
})
} else {
Err(errors)
}
}
}

impl From<FullyResolvedSubgraphs> for SupergraphConfig {
fn from(value: FullyResolvedSubgraphs) -> Self {
let subgraphs = BTreeMap::from_iter(value.subgraphs.into_iter().map(|(name, sdl)| {
(
name,
SubgraphConfig {
routing_url: None,
schema: SchemaSource::Sdl { sdl },
},
)
}));
SupergraphConfig::new(subgraphs, None)
}
}
Loading

0 comments on commit d73595f

Please sign in to comment.