diff --git a/Cargo.lock b/Cargo.lock index bb53b8cc4..e41df46e6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -152,9 +152,9 @@ dependencies = [ [[package]] name = "apollo-federation-types" -version = "0.13.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca8db61fb3171a1c0f1949a322f222af6c07d9180e9184f9ae6709a88e356e5c" +checksum = "c878d6a65db1ed770f90800962df612af8d374a0b6132343fe7d4a003b2d8c8e" dependencies = [ "camino", "log", diff --git a/Cargo.toml b/Cargo.toml index 8ea9e2b38..4e74b88c0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -59,7 +59,7 @@ apollo-parser = "0.7" apollo-encoder = "0.8" # https://github.com/apollographql/federation-rs -apollo-federation-types = "0.13.0" +apollo-federation-types = "0.13.1" # crates.io dependencies ariadne = "0.4" diff --git a/src/command/dev/do_dev.rs b/src/command/dev/do_dev.rs index 593704409..9d08c6bcd 100644 --- a/src/command/dev/do_dev.rs +++ b/src/command/dev/do_dev.rs @@ -1,4 +1,5 @@ use anyhow::{anyhow, Context}; +use apollo_federation_types::config::FederationVersion; use camino::Utf8PathBuf; use crossbeam_channel::bounded as sync_channel; @@ -10,6 +11,7 @@ use crate::utils::client::StudioClientConfig; use crate::{RoverError, RoverOutput, RoverResult}; use super::protocol::{FollowerChannel, FollowerMessenger, LeaderChannel, LeaderSession}; +use super::remote_subgraphs::RemoteSubgraphs; use super::router::RouterConfigHandler; use super::Dev; @@ -34,7 +36,22 @@ impl Dev { let leader_channel = LeaderChannel::new(); let follower_channel = FollowerChannel::new(); - // Read in Supergraph Config + // Read in Remote subgraphs + let remote_subgraphs = match &self.opts.supergraph_opts.graph_ref { + Some(graph_ref) => Some(RemoteSubgraphs::fetch( + &client_config.get_authenticated_client(&self.opts.plugin_opts.profile)?, + &self + .opts + .supergraph_opts + .federation_version + .clone() + .unwrap_or(FederationVersion::LatestFedTwo), + graph_ref, + )?), + None => None, + }; + + // Read in Local Supergraph Config let supergraph_config = if let Some(config_path) = &self.opts.supergraph_opts.supergraph_config_path { let config_content = Fs::read_file(config_path)?; @@ -43,6 +60,19 @@ impl Dev { None }; + // Merge Remote and Local Supergraph Configs + let supergraph_config = match remote_subgraphs { + Some(remote_subgraphs) => match supergraph_config { + Some(supergraph_config) => { + let mut merged_supergraph_config = remote_subgraphs.inner().clone(); + merged_supergraph_config.merge_subgraphs(&supergraph_config); + Some(merged_supergraph_config) + } + None => Some(remote_subgraphs.inner().clone()), + }, + None => supergraph_config, + }; + // Build a Rayon Thread pool let tp = rayon::ThreadPoolBuilder::new() .num_threads(1) diff --git a/src/command/dev/mod.rs b/src/command/dev/mod.rs index 8d81cf641..b057dc9ca 100644 --- a/src/command/dev/mod.rs +++ b/src/command/dev/mod.rs @@ -1,36 +1,42 @@ +use std::net::IpAddr; + +use apollo_federation_types::config::FederationVersion; +use camino::Utf8PathBuf; +use clap::Parser; +use rover_client::shared::GraphRef; +use serde::Serialize; + +use crate::options::{OptionalSubgraphOpts, PluginOpts}; + #[cfg(feature = "composition-js")] mod compose; #[cfg(feature = "composition-js")] -mod introspect; +mod do_dev; #[cfg(feature = "composition-js")] -mod router; +mod introspect; #[cfg(feature = "composition-js")] -mod schema; +mod protocol; #[cfg(feature = "composition-js")] -mod protocol; +mod remote_subgraphs; #[cfg(feature = "composition-js")] -mod netstat; +mod router; #[cfg(feature = "composition-js")] -mod watcher; +mod schema; #[cfg(feature = "composition-js")] -mod do_dev; +mod netstat; #[cfg(not(feature = "composition-js"))] mod no_dev; -use crate::options::{OptionalSubgraphOpts, PluginOpts}; -use std::net::IpAddr; - -use camino::Utf8PathBuf; -use clap::Parser; -use serde::Serialize; +#[cfg(feature = "composition-js")] +mod watcher; #[derive(Debug, Serialize, Parser)] pub struct Dev { @@ -86,6 +92,19 @@ pub struct SupergraphOpts { conflicts_with_all = ["subgraph_name", "subgraph_url", "subgraph_schema_path"] )] supergraph_config_path: Option, + + /// A [`GraphRef`] that is accessible in Apollo Studio. + /// This is used to initialize your supergraph with the values contained in this variant. + /// + /// This is analogous to providing a supergraph.yaml file with references to your graph variant in studio. + /// + /// If used in conjunction with `--supergraph-config`, the values presented in the supergraph.yaml will take precedence over these values. + #[arg(long = "graph-ref")] + graph_ref: Option, + + /// The version of Apollo Federation to use for composition + #[arg(long = "federation-version")] + federation_version: Option, } lazy_static::lazy_static! { diff --git a/src/command/dev/protocol/leader.rs b/src/command/dev/protocol/leader.rs index 1c5339cf0..aa4b10750 100644 --- a/src/command/dev/protocol/leader.rs +++ b/src/command/dev/protocol/leader.rs @@ -112,7 +112,7 @@ impl LeaderSession { let mut router_runner = RouterRunner::new( router_config_handler.get_supergraph_schema_path(), router_config_handler.get_router_config_path(), - plugin_opts, + plugin_opts.clone(), router_socket_addr, router_config_handler.get_router_listen_path(), override_install_path, @@ -403,6 +403,7 @@ impl LeaderSession { .map(|((name, url), sdl)| SubgraphDefinition::new(name, url.to_string(), sdl)) .collect::>() .into(); + supergraph_config.set_federation_version(self.federation_version.clone()); supergraph_config } diff --git a/src/command/dev/remote_subgraphs.rs b/src/command/dev/remote_subgraphs.rs new file mode 100644 index 000000000..1e585db74 --- /dev/null +++ b/src/command/dev/remote_subgraphs.rs @@ -0,0 +1,55 @@ +use apollo_federation_types::config::{ + FederationVersion, SchemaSource, SubgraphConfig, SupergraphConfig, +}; +use rover_client::{ + blocking::StudioClient, + operations::subgraph::{self, list::SubgraphListInput}, + shared::GraphRef, +}; + +use crate::RoverResult; + +/// Nominal type that captures the behavior of collecting remote subgraphs into a +/// [`SupergraphConfig`] representation +#[derive(Clone, Debug)] +pub struct RemoteSubgraphs(SupergraphConfig); + +impl RemoteSubgraphs { + /// Fetches [`RemoteSubgraphs`] from Studio + pub fn fetch( + client: &StudioClient, + federation_version: &FederationVersion, + graph_ref: &GraphRef, + ) -> RoverResult { + let subgraphs = subgraph::list::run( + SubgraphListInput { + graph_ref: graph_ref.clone(), + }, + client, + )?; + let subgraphs = subgraphs + .subgraphs + .iter() + .map(|subgraph| { + ( + subgraph.name.clone(), + SubgraphConfig { + routing_url: subgraph.url.clone(), + schema: SchemaSource::Subgraph { + graphref: graph_ref.clone().to_string(), + subgraph: subgraph.name.clone(), + }, + }, + ) + }) + .collect(); + let supergraph_config = SupergraphConfig::new(subgraphs, Some(federation_version.clone())); + let remote_subgraphs = RemoteSubgraphs(supergraph_config); + Ok(remote_subgraphs) + } + + /// Provides a reference to the inner value of this representation + pub fn inner(&self) -> &SupergraphConfig { + &self.0 + } +}