Skip to content

Commit

Permalink
ROVER-69 Optimise --graph-ref uses
Browse files Browse the repository at this point in the history
Rather than fetching each subgraph one at a time,
make a single GraphQL request to get them all at
once.

This combines the work from #1985
  • Loading branch information
jonathanrainer committed Jul 25, 2024
1 parent 8650523 commit 219ff26
Show file tree
Hide file tree
Showing 9 changed files with 257 additions and 13 deletions.
58 changes: 58 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ backtrace = "0.3"
backoff = "0.4"
base64 = "0.22"
billboard = "0.2"
buildstructor = "0.5.4"
cargo_metadata = "0.18"
calm_io = "0.1"
camino = "1"
Expand All @@ -80,6 +81,7 @@ ci_info = "0.14"
console = "0.15"
crossbeam-channel = "0.5"
ctrlc = "3"
derive-getters = "0.4.0"
dialoguer = "0.11"
directories-next = "2.0"
flate2 = "1"
Expand Down
2 changes: 2 additions & 0 deletions crates/rover-client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ apollo-federation-types = { workspace = true }
apollo-parser = { workspace = true }
apollo-encoder = { workspace = true }
backoff = { workspace = true }
buildstructor = { workspace = true }
chrono = { workspace = true, features = ["serde"] }
derive-getters = { workspace = true }
git-url-parse = { workspace = true }
git2 = { workspace = true, features = [
"vendored-openssl",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
query SubgraphFetchAllQuery($graph_ref: ID!) {
variant(ref: $graph_ref) {
__typename
... on GraphVariant {
subgraphs {
name
url
activePartialSchema {
sdl
}
}
}
}
}
5 changes: 5 additions & 0 deletions crates/rover-client/src/operations/subgraph/fetch_all/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
mod runner;
mod types;

pub use runner::run;
pub use types::SubgraphFetchAllInput;
133 changes: 133 additions & 0 deletions crates/rover-client/src/operations/subgraph/fetch_all/runner.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
use super::types::*;
use crate::blocking::StudioClient;
use crate::operations::config::is_federated::{self, IsFederatedInput};
use crate::RoverClientError;

use graphql_client::*;

#[derive(GraphQLQuery)]
// The paths are relative to the directory where your `Cargo.toml` is located.
// Both json and the GraphQL schema language are supported as sources for the schema
#[graphql(
query_path = "src/operations/subgraph/fetch_all/fetch_all_query.graphql",
schema_path = ".schema/schema.graphql",
response_derives = "Eq, PartialEq, Debug, Serialize, Deserialize",
deprecated = "warn"
)]
/// This struct is used to generate the module containing `Variables` and
/// `ResponseData` structs.
/// Snake case of this name is the mod name. i.e. subgraph_fetch_all_query
pub(crate) struct SubgraphFetchAllQuery;

/// Fetches a schema from apollo studio and returns its SDL (String)
pub fn run(
input: SubgraphFetchAllInput,
client: &StudioClient,
) -> Result<Vec<Subgraph>, RoverClientError> {
// This response is used to check whether or not the current graph is federated.
let is_federated = is_federated::run(
IsFederatedInput {
graph_ref: input.graph_ref.clone(),
},
client,
)?;
if !is_federated {
return Err(RoverClientError::ExpectedFederatedGraph {
graph_ref: input.graph_ref,
can_operation_convert: false,
});
}
let variables = input.clone().into();
let response_data = client.post::<SubgraphFetchAllQuery>(variables)?;
get_subgraphs_from_response_data(input, response_data)
}

fn get_subgraphs_from_response_data(
input: SubgraphFetchAllInput,
response_data: SubgraphFetchAllResponseData,
) -> Result<Vec<Subgraph>, RoverClientError> {
if let Some(maybe_variant) = response_data.variant {
match maybe_variant {
SubgraphFetchAllGraphVariant::GraphVariant(variant) => {
if let Some(subgraphs) = variant.subgraphs {
Ok(subgraphs
.into_iter()
.map(|subgraph| {
Subgraph::builder()
.name(subgraph.name.clone())
.and_url(subgraph.url)
.sdl(subgraph.active_partial_schema.sdl)
.build()
})
.collect())
} else {
Err(RoverClientError::ExpectedFederatedGraph {
graph_ref: input.graph_ref,
can_operation_convert: true,
})
}
}
_ => Err(RoverClientError::InvalidGraphRef),
}
} else {
Err(RoverClientError::GraphNotFound {
graph_ref: input.graph_ref,
})
}
}

#[cfg(test)]
mod tests {
use super::*;
use crate::shared::GraphRef;
use serde_json::json;

#[test]
fn get_services_from_response_data_works() {
let sdl = "extend type User @key(fields: \"id\") {\n id: ID! @external\n age: Int\n}\n"
.to_string();
let url = "http://my.subgraph.com".to_string();
let input = mock_input();
let json_response = json!({
"variant": {
"__typename": "GraphVariant",
"subgraphs": [
{
"name": "accounts",
"url": &url,
"activePartialSchema": {
"sdl": &sdl
}
},
]
}
});
let data: SubgraphFetchAllResponseData = serde_json::from_value(json_response).unwrap();
let expected_subgraph = Subgraph::builder()
.url(url)
.sdl(sdl)
.name("accounts".to_string())
.build();
let output = get_subgraphs_from_response_data(input, data);

assert!(output.is_ok());
assert_eq!(output.unwrap(), vec![expected_subgraph]);
}

#[test]
fn get_services_from_response_data_errs_with_no_variant() {
let json_response = json!({ "variant": null });
let data: SubgraphFetchAllResponseData = serde_json::from_value(json_response).unwrap();
let output = get_subgraphs_from_response_data(mock_input(), data);
assert!(output.is_err());
}

fn mock_input() -> SubgraphFetchAllInput {
let graph_ref = GraphRef {
name: "mygraph".to_string(),
variant: "current".to_string(),
};

SubgraphFetchAllInput { graph_ref }
}
}
31 changes: 31 additions & 0 deletions crates/rover-client/src/operations/subgraph/fetch_all/types.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
use buildstructor::Builder;
use derive_getters::Getters;

use crate::shared::GraphRef;

use super::runner::subgraph_fetch_all_query;

pub(crate) type SubgraphFetchAllResponseData = subgraph_fetch_all_query::ResponseData;
pub(crate) type SubgraphFetchAllGraphVariant =
subgraph_fetch_all_query::SubgraphFetchAllQueryVariant;
pub(crate) type QueryVariables = subgraph_fetch_all_query::Variables;

#[derive(Debug, Clone, Eq, PartialEq)]
pub struct SubgraphFetchAllInput {
pub graph_ref: GraphRef,
}

impl From<SubgraphFetchAllInput> for QueryVariables {
fn from(input: SubgraphFetchAllInput) -> Self {
Self {
graph_ref: input.graph_ref.to_string(),
}
}
}

#[derive(Clone, Builder, Debug, Eq, Getters, PartialEq)]
pub struct Subgraph {
name: String,
url: Option<String>,
sdl: String,
}
3 changes: 3 additions & 0 deletions crates/rover-client/src/operations/subgraph/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ pub mod check;
/// "subgraph fetch" command execution
pub mod fetch;

/// "subgraph fetch_all" command execution
pub mod fetch_all;

/// "subgraph publish" command execution
pub mod publish;

Expand Down
22 changes: 9 additions & 13 deletions src/utils/supergraph_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@ use apollo_federation_types::config::{
use apollo_parser::{cst, Parser};
use rayon::iter::{IntoParallelIterator, ParallelIterator};

use rover_client::blocking::GraphQLClient;
use rover_client::blocking::{GraphQLClient, StudioClient};
use rover_client::operations::subgraph;
use rover_client::operations::subgraph::fetch::SubgraphFetchInput;
use rover_client::operations::subgraph::introspect::SubgraphIntrospectInput;
use rover_client::operations::subgraph::list::SubgraphListInput;
use rover_client::operations::subgraph::{fetch, introspect};
use rover_client::operations::subgraph::fetch_all::SubgraphFetchAllInput;
use rover_client::shared::GraphRef;
use rover_client::RoverClientError;
use rover_std::{Fs, Style};
Expand All @@ -32,30 +33,25 @@ pub struct RemoteSubgraphs(SupergraphConfig);
impl RemoteSubgraphs {
/// Fetches [`RemoteSubgraphs`] from Studio
pub fn fetch(
client_config: &StudioClientConfig,
profile_opt: &ProfileOpt,
client: &StudioClient,
federation_version: &FederationVersion,
graph_ref: &GraphRef,
) -> RoverResult<RemoteSubgraphs> {
let client = &client_config.get_authenticated_client(profile_opt)?;

let subgraphs = subgraph::list::run(
SubgraphListInput {
let subgraphs = subgraph::fetch_all::run(
SubgraphFetchAllInput {
graph_ref: graph_ref.clone(),
},
client,
)?;
let subgraphs = subgraphs
.subgraphs
.iter()
.map(|subgraph| {
(
subgraph.name.clone(),
subgraph.name().clone(),
SubgraphConfig {
routing_url: subgraph.url.clone(),
schema: SchemaSource::Subgraph {
graphref: graph_ref.clone().to_string(),
subgraph: subgraph.name.clone(),
routing_url: subgraph.url().clone(),
schema: SchemaSource::Sdl {
sdl: subgraph.sdl().clone(),
},
},
)
Expand Down

0 comments on commit 219ff26

Please sign in to comment.