From 842175fc301ef696f60c84bfb086dcef3986dac9 Mon Sep 17 00:00:00 2001 From: Chris Lenfest Date: Mon, 29 Apr 2024 11:52:42 -0500 Subject: [PATCH 01/69] set context branch --- Cargo.lock | 27 +- apollo-router/Cargo.toml | 2 +- apollo-router/build/main.rs | 22 +- apollo-router/src/query_planner/execution.rs | 7 + apollo-router/src/query_planner/rewrites.rs | 1 + apollo-router/tests/set_context.rs | 430 +++++++++++++++++++ 6 files changed, 475 insertions(+), 14 deletions(-) create mode 100644 apollo-router/tests/set_context.rs diff --git a/Cargo.lock b/Cargo.lock index 347edbedc9..70afec89c8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -351,7 +351,7 @@ dependencies = [ "regex", "reqwest", "rhai", - "router-bridge", + "router-bridge 0.5.20+v2.7.4", "rstack", "rust-embed", "rustls", @@ -5702,6 +5702,29 @@ dependencies = [ "paste", ] +[[package]] +name = "router-bridge" +version = "0.5.20+v2.7.4" +dependencies = [ + "anyhow", + "async-channel 1.9.0", + "deno_console", + "deno_core", + "deno_crypto", + "deno_url", + "deno_web", + "deno_webidl", + "rand 0.8.5", + "serde", + "serde_json", + "thiserror", + "tokio", + "tower", + "tower-service", + "tracing", + "which", +] + [[package]] name = "router-bridge" version = "0.5.20+v2.7.4" @@ -5742,7 +5765,7 @@ dependencies = [ "libfuzzer-sys", "log", "reqwest", - "router-bridge", + "router-bridge 0.5.20+v2.7.4 (registry+https://github.com/rust-lang/crates.io-index)", "schemars", "serde", "serde_json", diff --git a/apollo-router/Cargo.toml b/apollo-router/Cargo.toml index 54f8222efc..d1d895eab8 100644 --- a/apollo-router/Cargo.toml +++ b/apollo-router/Cargo.toml @@ -183,7 +183,7 @@ regex = "1.10.3" reqwest.workspace = true # note: this dependency should _always_ be pinned, prefix the version with an `=` -router-bridge = "=0.5.20+v2.7.4" +router-bridge = { path = "/Users/clenfest/src/federation-rs/federation-2/router-bridge" } rust-embed = "8.2.0" rustls = "0.21.11" diff --git a/apollo-router/build/main.rs b/apollo-router/build/main.rs index 763d894df0..844a2fab37 100644 --- a/apollo-router/build/main.rs +++ b/apollo-router/build/main.rs @@ -10,23 +10,23 @@ fn main() -> Result<(), Box> { ) .expect("could not parse Cargo.toml"); - let router_bridge = cargo_manifest + let _router_bridge = cargo_manifest .get("dependencies") .expect("Cargo.toml does not contain dependencies") .as_object() .expect("Cargo.toml dependencies key is not an object") .get("router-bridge") .expect("Cargo.toml dependencies does not have an entry for router-bridge"); - let router_bridge_version = router_bridge - .as_str() - .or_else(|| { - router_bridge - .as_object() - .and_then(|o| o.get("version")) - .and_then(|version| version.as_str()) - }) - .expect("router-bridge does not have a version"); - + // let router_bridge_version = router_bridge + // .as_str() + // .or_else(|| { + // router_bridge + // .as_object() + // .and_then(|o| o.get("version")) + // .and_then(|version| version.as_str()) + // }) + // .expect("router-bridge does not have a version"); + let router_bridge_version = "0.5.20+v2.8.0"; let mut it = router_bridge_version.split('+'); let _ = it.next(); let fed_version = it.next().expect("invalid router-bridge version format"); diff --git a/apollo-router/src/query_planner/execution.rs b/apollo-router/src/query_planner/execution.rs index 801069afd1..d4c2254d0e 100644 --- a/apollo-router/src/query_planner/execution.rs +++ b/apollo-router/src/query_planner/execution.rs @@ -123,6 +123,7 @@ impl PlanNode { match self { PlanNode::Sequence { nodes } => { + tracing::debug!("here PlanNode::Sequence"); value = parent_value.clone(); errors = Vec::new(); async { @@ -147,6 +148,7 @@ impl PlanNode { .await } PlanNode::Parallel { nodes } => { + tracing::debug!("here PlanNode::Parallel"); value = Value::default(); errors = Vec::new(); async { @@ -175,6 +177,8 @@ impl PlanNode { .await } PlanNode::Flatten(FlattenNode { path, node }) => { + tracing::debug!("here PlanNode::FlattenNode"); + // Note that the span must be `info` as we need to pick this up in apollo tracing let current_dir = current_dir.join(path.remove_empty_key_root()); let (v, err) = node @@ -218,6 +222,7 @@ impl PlanNode { value = Value::default(); } PlanNode::Fetch(fetch_node) => { + tracing::debug!("here PlanNode::Fetch"); let fetch_time_offset = parameters.context.created_at.elapsed().as_nanos() as i64; @@ -230,9 +235,11 @@ impl PlanNode { .get::() .is_some() { + tracing::debug!("fetch node errors"); value = Value::Object(Object::default()); errors = Vec::new(); } else { + tracing::debug!("fetch node fetching"); let (v, e) = fetch_node .fetch_node(parameters, parent_value, current_dir) .instrument(tracing::info_span!( diff --git a/apollo-router/src/query_planner/rewrites.rs b/apollo-router/src/query_planner/rewrites.rs index 94453630df..62497f2c5d 100644 --- a/apollo-router/src/query_planner/rewrites.rs +++ b/apollo-router/src/query_planner/rewrites.rs @@ -58,6 +58,7 @@ impl DataRewrite { if let Some((parent, PathElement::Key(k, _))) = split_path_last_element(&setter.path) { + tracing::info!("REWRITES 1 {} {}", k, parent); data.select_values_and_paths_mut(schema, &parent, |_path, obj| { if let Some(value) = obj.get_mut(k) { *value = setter.set_value_to.clone() diff --git a/apollo-router/tests/set_context.rs b/apollo-router/tests/set_context.rs new file mode 100644 index 0000000000..406aa9dcdb --- /dev/null +++ b/apollo-router/tests/set_context.rs @@ -0,0 +1,430 @@ +//! +//! Please ensure that any tests added to this file use the tokio multi-threaded test executor. +//! + +use apollo_compiler::execution::JsonMap; +use apollo_router::graphql::Request; +use apollo_router::graphql::Response; +use apollo_router::plugin::test::MockSubgraph; +use apollo_router::services::supergraph; +use apollo_router::MockedSubgraphs; +use apollo_router::TestHarness; +use serde::Deserialize; +use serde_json::json; +use tower::ServiceExt; + +#[derive(Deserialize)] +struct SubgraphMock { + mocks: Vec, +} + +#[derive(Deserialize)] +struct RequestAndResponse { + request: Request, + response: Response, +} + +#[tokio::test(flavor = "multi_thread")] +async fn test_set_context() { + let harness = setup_from_mocks( + json! {{ + "experimental_type_conditioned_fetching": true, + // will make debugging easier + "plugins": { + "experimental.expose_query_plan": true + }, + "include_subgraph_errors": { + "all": true + } + }}, + &[ + ( + "searchSubgraph", + include_str!("fixtures/type_conditions/search.json"), + ), + ( + "artworkSubgraph", + include_str!("fixtures/type_conditions/artwork.json"), + ), + ], + ); + let supergraph_service = harness.build_supergraph().await.unwrap(); + let mut variables = JsonMap::new(); + variables.insert("movieResultParam", "movieResultEnabled".into()); + variables.insert("articleResultParam", "articleResultEnabled".into()); + let request = supergraph::Request::fake_builder() + .query(QUERY.to_string()) + .header("Apollo-Expose-Query-Plan", "true") + .variables(variables) + .build() + .expect("expecting valid request"); + + let response = supergraph_service + .oneshot(request) + .await + .unwrap() + .next_response() + .await + .unwrap(); + + insta::assert_json_snapshot!(response); +} + +#[tokio::test(flavor = "multi_thread")] +async fn test_type_conditions_enabled_generate_query_fragments() { + let harness = setup_from_mocks( + json! {{ + "experimental_type_conditioned_fetching": true, + "supergraph": { + "generate_query_fragments": true + }, + // will make debugging easier + "plugins": { + "experimental.expose_query_plan": true + }, + "include_subgraph_errors": { + "all": true + } + }}, + &[ + ( + "searchSubgraph", + include_str!("fixtures/type_conditions/search_query_fragments_enabled.json"), + ), + ( + "artworkSubgraph", + include_str!("fixtures/type_conditions/artwork_query_fragments_enabled.json"), + ), + ], + ); + let supergraph_service = harness.build_supergraph().await.unwrap(); + let mut variables = JsonMap::new(); + variables.insert("movieResultParam", "movieResultEnabled".into()); + variables.insert("articleResultParam", "articleResultEnabled".into()); + let request = supergraph::Request::fake_builder() + .query(QUERY.to_string()) + .header("Apollo-Expose-Query-Plan", "true") + .variables(variables) + .build() + .expect("expecting valid request"); + + let response = supergraph_service + .oneshot(request) + .await + .unwrap() + .next_response() + .await + .unwrap(); + + insta::assert_json_snapshot!(response); +} + +#[tokio::test(flavor = "multi_thread")] +async fn test_type_conditions_enabled_list_of_list() { + let harness = setup_from_mocks( + json! {{ + "experimental_type_conditioned_fetching": true, + // will make debugging easier + "plugins": { + "experimental.expose_query_plan": true + }, + "include_subgraph_errors": { + "all": true + } + }}, + &[ + ( + "searchSubgraph", + include_str!("fixtures/type_conditions/search_list_of_list.json"), + ), + ( + "artworkSubgraph", + include_str!("fixtures/type_conditions/artwork.json"), + ), + ], + ); + let supergraph_service = harness.build_supergraph().await.unwrap(); + let mut variables = JsonMap::new(); + variables.insert("movieResultParam", "movieResultEnabled".into()); + variables.insert("articleResultParam", "articleResultEnabled".into()); + let request = supergraph::Request::fake_builder() + .query(QUERY_LIST_OF_LIST.to_string()) + .header("Apollo-Expose-Query-Plan", "true") + .variables(variables) + .build() + .expect("expecting valid request"); + + let response = supergraph_service + .oneshot(request) + .await + .unwrap() + .next_response() + .await + .unwrap(); + + insta::assert_json_snapshot!(response); +} + +// one last to make sure unnesting is correct +#[tokio::test(flavor = "multi_thread")] +async fn test_type_conditions_enabled_list_of_list_of_list() { + let harness = setup_from_mocks( + json! {{ + "experimental_type_conditioned_fetching": true, + // will make debugging easier + "plugins": { + "experimental.expose_query_plan": true + }, + "include_subgraph_errors": { + "all": true + } + }}, + &[ + ( + "searchSubgraph", + include_str!("fixtures/type_conditions/search_list_of_list_of_list.json"), + ), + ( + "artworkSubgraph", + include_str!("fixtures/type_conditions/artwork.json"), + ), + ], + ); + let supergraph_service = harness.build_supergraph().await.unwrap(); + let mut variables = JsonMap::new(); + variables.insert("movieResultParam", "movieResultEnabled".into()); + variables.insert("articleResultParam", "articleResultEnabled".into()); + let request = supergraph::Request::fake_builder() + .query(QUERY_LIST_OF_LIST_OF_LIST.to_string()) + .header("Apollo-Expose-Query-Plan", "true") + .variables(variables) + .build() + .expect("expecting valid request"); + + let response = supergraph_service + .oneshot(request) + .await + .unwrap() + .next_response() + .await + .unwrap(); + + insta::assert_json_snapshot!(response); +} + +#[tokio::test(flavor = "multi_thread")] +async fn test_type_conditions_disabled() { + let harness = setup_from_mocks( + json! {{ + "experimental_type_conditioned_fetching": false, + // will make debugging easier + "plugins": { + "experimental.expose_query_plan": true + }, + "include_subgraph_errors": { + "all": true + } + }}, + &[ + ( + "searchSubgraph", + include_str!("fixtures/type_conditions/search.json"), + ), + ( + "artworkSubgraph", + include_str!("fixtures/type_conditions/artwork_disabled.json"), + ), + ], + ); + let supergraph_service = harness.build_supergraph().await.unwrap(); + let mut variables = JsonMap::new(); + variables.insert("movieResultParam", "movieResultDisabled".into()); + variables.insert("articleResultParam", "articleResultDisabled".into()); + let request = supergraph::Request::fake_builder() + .query(QUERY.to_string()) + .header("Apollo-Expose-Query-Plan", "true") + .build() + .expect("expecting valid request"); + + let response = supergraph_service + .oneshot(request) + .await + .unwrap() + .next_response() + .await + .unwrap(); + + insta::assert_json_snapshot!(response); +} + +#[tokio::test(flavor = "multi_thread")] +async fn test_type_conditions_enabled_shouldnt_make_article_fetch() { + let harness = setup_from_mocks( + json! {{ + "experimental_type_conditioned_fetching": true, + // will make debugging easier + "plugins": { + "experimental.expose_query_plan": true + }, + "include_subgraph_errors": { + "all": true + } + }}, + &[ + ( + "searchSubgraph", + include_str!("fixtures/type_conditions/search_no_articles.json"), + ), + ( + "artworkSubgraph", + include_str!("fixtures/type_conditions/artwork_no_articles.json"), + ), + ], + ); + let supergraph_service = harness.build_supergraph().await.unwrap(); + let mut variables = JsonMap::new(); + variables.insert("movieResultParam", "movieResultEnabled".into()); + variables.insert("articleResultParam", "articleResultEnabled".into()); + let request = supergraph::Request::fake_builder() + .query(QUERY.to_string()) + .header("Apollo-Expose-Query-Plan", "true") + .variables(variables) + .build() + .expect("expecting valid request"); + + let response = supergraph_service + .oneshot(request) + .await + .unwrap() + .next_response() + .await + .unwrap(); + + insta::assert_json_snapshot!(response); +} + +fn setup_from_mocks( + configuration: serde_json::Value, + mocks: &[(&'static str, &'static str)], +) -> TestHarness<'static> { + let mut mocked_subgraphs = MockedSubgraphs::default(); + + for (name, m) in mocks { + let subgraph_mock: SubgraphMock = serde_json::from_str(m).unwrap(); + + let mut builder = MockSubgraph::builder(); + + for mock in subgraph_mock.mocks { + builder = builder.with_json( + serde_json::to_value(mock.request).unwrap(), + serde_json::to_value(mock.response).unwrap(), + ); + } + + mocked_subgraphs.insert(name, builder.build()); + } + + let schema = include_str!("fixtures/type_conditions/type_conditions.graphql"); + TestHarness::builder() + .try_log_level("info") + .configuration_json(configuration) + .unwrap() + .schema(schema) + .extra_plugin(mocked_subgraphs) +} + +static QUERY: &str = r#" +query Search($movieResultParam: String, $articleResultParam: String) { + search { + ... on MovieResult { + sections { + ... on EntityCollectionSection { + id + title + artwork(params: $movieResultParam) + } + ... on GallerySection { + artwork(params: $movieResultParam) + id + } + } + id + } + ... on ArticleResult { + id + sections { + ... on GallerySection { + artwork(params: $articleResultParam) + } + ... on EntityCollectionSection { + artwork(params: $articleResultParam) + title + } + } + } + } +}"#; + +static QUERY_LIST_OF_LIST: &str = r#" +query Search($movieResultParam: String, $articleResultParam: String) { + searchListOfList { + ... on MovieResult { + sections { + ... on EntityCollectionSection { + id + title + artwork(params: $movieResultParam) + } + ... on GallerySection { + artwork(params: $movieResultParam) + id + } + } + id + } + ... on ArticleResult { + id + sections { + ... on GallerySection { + artwork(params: $articleResultParam) + } + ... on EntityCollectionSection { + artwork(params: $articleResultParam) + title + } + } + } + } +}"#; + +static QUERY_LIST_OF_LIST_OF_LIST: &str = r#" +query Search($movieResultParam: String, $articleResultParam: String) { + searchListOfListOfList { + ... on MovieResult { + sections { + ... on EntityCollectionSection { + id + title + artwork(params: $movieResultParam) + } + ... on GallerySection { + artwork(params: $movieResultParam) + id + } + } + id + } + ... on ArticleResult { + id + sections { + ... on GallerySection { + artwork(params: $articleResultParam) + } + ... on EntityCollectionSection { + artwork(params: $articleResultParam) + title + } + } + } + } +}"#; From 88d898a2392284e855ca4c42ac9d633d49ab48cd Mon Sep 17 00:00:00 2001 From: o0Ignition0o Date: Mon, 29 Apr 2024 19:59:55 +0200 Subject: [PATCH 02/69] wip --- Cargo.lock | 29 +- apollo-router/Cargo.toml | 2 +- .../src/plugin/test/mock/subgraph.rs | 1 + apollo-router/src/query_planner/execution.rs | 2 +- apollo-router/src/query_planner/fetch.rs | 31 +- .../tests/fixtures/set_context/one.json | 39 ++ .../fixtures/set_context/supergraph.graphql | 87 +++++ .../tests/fixtures/set_context/two.json | 20 + apollo-router/tests/set_context.rs | 350 +----------------- fuzz/Cargo.toml | 2 +- 10 files changed, 192 insertions(+), 371 deletions(-) create mode 100644 apollo-router/tests/fixtures/set_context/one.json create mode 100644 apollo-router/tests/fixtures/set_context/supergraph.graphql create mode 100644 apollo-router/tests/fixtures/set_context/two.json diff --git a/Cargo.lock b/Cargo.lock index 70afec89c8..acd4363f3c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -351,7 +351,7 @@ dependencies = [ "regex", "reqwest", "rhai", - "router-bridge 0.5.20+v2.7.4", + "router-bridge", "rstack", "rust-embed", "rustls", @@ -5725,31 +5725,6 @@ dependencies = [ "which", ] -[[package]] -name = "router-bridge" -version = "0.5.20+v2.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5179aaf2c3c9ca2d76e2c5f6fbfe30a4e1d4c64cae0d269d0395b351f4993fbb" -dependencies = [ - "anyhow", - "async-channel 1.9.0", - "deno_console", - "deno_core", - "deno_crypto", - "deno_url", - "deno_web", - "deno_webidl", - "rand 0.8.5", - "serde", - "serde_json", - "thiserror", - "tokio", - "tower", - "tower-service", - "tracing", - "which", -] - [[package]] name = "router-fuzz" version = "0.0.0" @@ -5765,7 +5740,7 @@ dependencies = [ "libfuzzer-sys", "log", "reqwest", - "router-bridge 0.5.20+v2.7.4 (registry+https://github.com/rust-lang/crates.io-index)", + "router-bridge", "schemars", "serde", "serde_json", diff --git a/apollo-router/Cargo.toml b/apollo-router/Cargo.toml index d1d895eab8..1c5641a618 100644 --- a/apollo-router/Cargo.toml +++ b/apollo-router/Cargo.toml @@ -183,7 +183,7 @@ regex = "1.10.3" reqwest.workspace = true # note: this dependency should _always_ be pinned, prefix the version with an `=` -router-bridge = { path = "/Users/clenfest/src/federation-rs/federation-2/router-bridge" } +router-bridge = { path = "../../federation-rs/federation-2/router-bridge" } rust-embed = "8.2.0" rustls = "0.21.11" diff --git a/apollo-router/src/plugin/test/mock/subgraph.rs b/apollo-router/src/plugin/test/mock/subgraph.rs index 80149de1d2..bc156265dd 100644 --- a/apollo-router/src/plugin/test/mock/subgraph.rs +++ b/apollo-router/src/plugin/test/mock/subgraph.rs @@ -173,6 +173,7 @@ impl Service for MockSubgraph { } } + dbg!(&body); let response = if let Some(response) = self.mocks.get(body) { // Build an http Response let mut http_response_builder = http::Response::builder().status(StatusCode::OK); diff --git a/apollo-router/src/query_planner/execution.rs b/apollo-router/src/query_planner/execution.rs index d4c2254d0e..4addf771a3 100644 --- a/apollo-router/src/query_planner/execution.rs +++ b/apollo-router/src/query_planner/execution.rs @@ -178,7 +178,7 @@ impl PlanNode { } PlanNode::Flatten(FlattenNode { path, node }) => { tracing::debug!("here PlanNode::FlattenNode"); - + // Note that the span must be `info` as we need to pick this up in apollo tracing let current_dir = current_dir.join(path.remove_empty_key_root()); let (v, err) = node diff --git a/apollo-router/src/query_planner/fetch.rs b/apollo-router/src/query_planner/fetch.rs index d10f47c7ab..2f6cb69a7d 100644 --- a/apollo-router/src/query_planner/fetch.rs +++ b/apollo-router/src/query_planner/fetch.rs @@ -7,6 +7,7 @@ use apollo_compiler::validation::Valid; use apollo_compiler::ExecutableDocument; use apollo_compiler::NodeStr; use indexmap::IndexSet; +use json_ext::PathElement; use serde::Deserialize; use serde::Serialize; use tower::ServiceExt; @@ -15,6 +16,8 @@ use tracing::Instrument; use super::execution::ExecutionParameters; use super::rewrites; +use super::rewrites::DataKeyRenamer; +use super::rewrites::DataRewrite; use super::selection::execute_selection_set; use super::selection::Selection; use crate::error::Error; @@ -257,6 +260,32 @@ impl Variables { schema: &Schema, input_rewrites: &Option>, ) -> Option { + let input_rewrites = if let Some(ir) = input_rewrites { + Some( + ir.into_iter() + .map(|item| { + if let DataRewrite::KeyRenamer(dr) = item { + if dr.path == Path(vec![PathElement::Key("prop".to_string(), None)]) { + return DataRewrite::KeyRenamer({ + DataKeyRenamer { + path: Path(vec![ + PathElement::Key("t".to_string(), None), + PathElement::Key("prop".to_string(), None), + ]), + rename_key_to: dr.rename_key_to.clone() + } + }); + } + } + item.clone() + }) + .collect(), + ) + } else { + input_rewrites.clone() + }; + + dbg!(&input_rewrites); let body = request.body(); if !requires.is_empty() { let mut variables = Object::with_capacity(1 + variable_usages.len()); @@ -273,7 +302,7 @@ impl Variables { data.select_values_and_paths(schema, current_dir, |path, value| { let mut value = execute_selection_set(value, requires, schema, None); if value.as_object().map(|o| !o.is_empty()).unwrap_or(false) { - rewrites::apply_rewrites(schema, &mut value, input_rewrites); + rewrites::apply_rewrites(schema, &mut value, &input_rewrites); match values.get_index_of(&value) { Some(index) => { inverted_paths[index].push(path.clone()); diff --git a/apollo-router/tests/fixtures/set_context/one.json b/apollo-router/tests/fixtures/set_context/one.json new file mode 100644 index 0000000000..f753811bb3 --- /dev/null +++ b/apollo-router/tests/fixtures/set_context/one.json @@ -0,0 +1,39 @@ +{ + "mocks": [ + { + "request": { + "query": "query Query__one__0{t{prop id u{id}}}", + "operationName": "Query__one__0" + }, + "response": { + "data": { + "t": { + "prop": "prop value", + "id": "1", + "u": { + "id": "1" + } + } + } + } + }, + { + "request": { + "query": "query Query__one__0{t{prop id u{__typename id}}}", + "operationName": "Query__one__0" + }, + "response": { + "data": { + "t": { + "prop": "prop value", + "id": "1", + "u": { + "__typename": "U", + "id": "1" + } + } + } + } + } + ] +} diff --git a/apollo-router/tests/fixtures/set_context/supergraph.graphql b/apollo-router/tests/fixtures/set_context/supergraph.graphql new file mode 100644 index 0000000000..f2a4455ccc --- /dev/null +++ b/apollo-router/tests/fixtures/set_context/supergraph.graphql @@ -0,0 +1,87 @@ +schema + @link(url: "https://specs.apollo.dev/link/v1.0") + @link(url: "https://specs.apollo.dev/join/v0.4", for: EXECUTION) + @link(url: "https://specs.apollo.dev/context/v0.1", for: SECURITY) +{ + query: Query +} + +directive @context(name: String!) repeatable on INTERFACE | OBJECT | UNION + +directive @context__fromContext(field: String) on ARGUMENT_DEFINITION + +directive @join__directive(graphs: [join__Graph!], name: String!, args: join__DirectiveArguments) repeatable on SCHEMA | OBJECT | INTERFACE | FIELD_DEFINITION + +directive @join__enumValue(graph: join__Graph!) repeatable on ENUM_VALUE + +directive @join__field(graph: join__Graph, requires: join__FieldSet, provides: join__FieldSet, type: String, external: Boolean, override: String, usedOverridden: Boolean, overrideLabel: String, contextArguments: [join__ContextArgument!]) repeatable on FIELD_DEFINITION | INPUT_FIELD_DEFINITION + +directive @join__graph(name: String!, url: String!) on ENUM_VALUE + +directive @join__implements(graph: join__Graph!, interface: String!) repeatable on OBJECT | INTERFACE + +directive @join__type(graph: join__Graph!, key: join__FieldSet, extension: Boolean! = false, resolvable: Boolean! = true, isInterfaceObject: Boolean! = false) repeatable on OBJECT | INTERFACE | UNION | ENUM | INPUT_OBJECT | SCALAR + +directive @join__unionMember(graph: join__Graph!, member: String!) repeatable on UNION + +directive @link(url: String, as: String, for: link__Purpose, import: [link__Import]) repeatable on SCHEMA + +scalar context__context + +input join__ContextArgument { + name: String! + type: String! + context: String! + selection: join__FieldValue! +} + +scalar join__DirectiveArguments + +scalar join__FieldSet + +scalar join__FieldValue + +enum join__Graph { + ONE @join__graph(name: "one", url: "http://localhost:4001") + TWO @join__graph(name: "two", url: "http://localhost:4002") +} + +scalar link__Import + +enum link__Purpose { + """ + `SECURITY` features provide metadata necessary to securely resolve fields. + """ + SECURITY + + """ + `EXECUTION` features provide metadata necessary for operation execution. + """ + EXECUTION +} + +type Query + @join__type(graph: ONE) + @join__type(graph: TWO) +{ + t: T! @join__field(graph: ONE) + a: Int! @join__field(graph: TWO) +} + +type T + @join__type(graph: ONE, key: "id") + @context(name: "one__context") +{ + id: ID! + u: U! + prop: String! +} + +type U + @join__type(graph: ONE, key: "id") + @join__type(graph: TWO, key: "id") +{ + id: ID! + field: Int! @join__field(graph: ONE, contextArguments: [{context: "one__context", name: "a", type: "String!", selection: "{ prop }"}]) + k: String! @join__field(graph: TWO) +} \ No newline at end of file diff --git a/apollo-router/tests/fixtures/set_context/two.json b/apollo-router/tests/fixtures/set_context/two.json new file mode 100644 index 0000000000..986a310a9b --- /dev/null +++ b/apollo-router/tests/fixtures/set_context/two.json @@ -0,0 +1,20 @@ +{ + "mocks": [ + { + "request": { + "query": "query Query__two__2($representations:[_Any!]!){_entities(representations:$representations){...on U{k}}}", + "operationName": "Query__two__2", + "variables": { "representations": [{ "__typename": "U", "id": "1" }] } + }, + "response": { + "data": { + "_entities": [ + { + "k": "k value" + } + ] + } + } + } + ] +} diff --git a/apollo-router/tests/set_context.rs b/apollo-router/tests/set_context.rs index 406aa9dcdb..566e94741e 100644 --- a/apollo-router/tests/set_context.rs +++ b/apollo-router/tests/set_context.rs @@ -2,7 +2,6 @@ //! Please ensure that any tests added to this file use the tokio multi-threaded test executor. //! -use apollo_compiler::execution::JsonMap; use apollo_router::graphql::Request; use apollo_router::graphql::Response; use apollo_router::plugin::test::MockSubgraph; @@ -38,257 +37,15 @@ async fn test_set_context() { } }}, &[ - ( - "searchSubgraph", - include_str!("fixtures/type_conditions/search.json"), - ), - ( - "artworkSubgraph", - include_str!("fixtures/type_conditions/artwork.json"), - ), + ("one", include_str!("fixtures/set_context/one.json")), + ("two", include_str!("fixtures/set_context/two.json")), ], ); let supergraph_service = harness.build_supergraph().await.unwrap(); - let mut variables = JsonMap::new(); - variables.insert("movieResultParam", "movieResultEnabled".into()); - variables.insert("articleResultParam", "articleResultEnabled".into()); let request = supergraph::Request::fake_builder() .query(QUERY.to_string()) .header("Apollo-Expose-Query-Plan", "true") - .variables(variables) - .build() - .expect("expecting valid request"); - - let response = supergraph_service - .oneshot(request) - .await - .unwrap() - .next_response() - .await - .unwrap(); - - insta::assert_json_snapshot!(response); -} - -#[tokio::test(flavor = "multi_thread")] -async fn test_type_conditions_enabled_generate_query_fragments() { - let harness = setup_from_mocks( - json! {{ - "experimental_type_conditioned_fetching": true, - "supergraph": { - "generate_query_fragments": true - }, - // will make debugging easier - "plugins": { - "experimental.expose_query_plan": true - }, - "include_subgraph_errors": { - "all": true - } - }}, - &[ - ( - "searchSubgraph", - include_str!("fixtures/type_conditions/search_query_fragments_enabled.json"), - ), - ( - "artworkSubgraph", - include_str!("fixtures/type_conditions/artwork_query_fragments_enabled.json"), - ), - ], - ); - let supergraph_service = harness.build_supergraph().await.unwrap(); - let mut variables = JsonMap::new(); - variables.insert("movieResultParam", "movieResultEnabled".into()); - variables.insert("articleResultParam", "articleResultEnabled".into()); - let request = supergraph::Request::fake_builder() - .query(QUERY.to_string()) - .header("Apollo-Expose-Query-Plan", "true") - .variables(variables) - .build() - .expect("expecting valid request"); - - let response = supergraph_service - .oneshot(request) - .await - .unwrap() - .next_response() - .await - .unwrap(); - - insta::assert_json_snapshot!(response); -} - -#[tokio::test(flavor = "multi_thread")] -async fn test_type_conditions_enabled_list_of_list() { - let harness = setup_from_mocks( - json! {{ - "experimental_type_conditioned_fetching": true, - // will make debugging easier - "plugins": { - "experimental.expose_query_plan": true - }, - "include_subgraph_errors": { - "all": true - } - }}, - &[ - ( - "searchSubgraph", - include_str!("fixtures/type_conditions/search_list_of_list.json"), - ), - ( - "artworkSubgraph", - include_str!("fixtures/type_conditions/artwork.json"), - ), - ], - ); - let supergraph_service = harness.build_supergraph().await.unwrap(); - let mut variables = JsonMap::new(); - variables.insert("movieResultParam", "movieResultEnabled".into()); - variables.insert("articleResultParam", "articleResultEnabled".into()); - let request = supergraph::Request::fake_builder() - .query(QUERY_LIST_OF_LIST.to_string()) - .header("Apollo-Expose-Query-Plan", "true") - .variables(variables) - .build() - .expect("expecting valid request"); - - let response = supergraph_service - .oneshot(request) - .await - .unwrap() - .next_response() - .await - .unwrap(); - - insta::assert_json_snapshot!(response); -} - -// one last to make sure unnesting is correct -#[tokio::test(flavor = "multi_thread")] -async fn test_type_conditions_enabled_list_of_list_of_list() { - let harness = setup_from_mocks( - json! {{ - "experimental_type_conditioned_fetching": true, - // will make debugging easier - "plugins": { - "experimental.expose_query_plan": true - }, - "include_subgraph_errors": { - "all": true - } - }}, - &[ - ( - "searchSubgraph", - include_str!("fixtures/type_conditions/search_list_of_list_of_list.json"), - ), - ( - "artworkSubgraph", - include_str!("fixtures/type_conditions/artwork.json"), - ), - ], - ); - let supergraph_service = harness.build_supergraph().await.unwrap(); - let mut variables = JsonMap::new(); - variables.insert("movieResultParam", "movieResultEnabled".into()); - variables.insert("articleResultParam", "articleResultEnabled".into()); - let request = supergraph::Request::fake_builder() - .query(QUERY_LIST_OF_LIST_OF_LIST.to_string()) - .header("Apollo-Expose-Query-Plan", "true") - .variables(variables) - .build() - .expect("expecting valid request"); - - let response = supergraph_service - .oneshot(request) - .await - .unwrap() - .next_response() - .await - .unwrap(); - - insta::assert_json_snapshot!(response); -} - -#[tokio::test(flavor = "multi_thread")] -async fn test_type_conditions_disabled() { - let harness = setup_from_mocks( - json! {{ - "experimental_type_conditioned_fetching": false, - // will make debugging easier - "plugins": { - "experimental.expose_query_plan": true - }, - "include_subgraph_errors": { - "all": true - } - }}, - &[ - ( - "searchSubgraph", - include_str!("fixtures/type_conditions/search.json"), - ), - ( - "artworkSubgraph", - include_str!("fixtures/type_conditions/artwork_disabled.json"), - ), - ], - ); - let supergraph_service = harness.build_supergraph().await.unwrap(); - let mut variables = JsonMap::new(); - variables.insert("movieResultParam", "movieResultDisabled".into()); - variables.insert("articleResultParam", "articleResultDisabled".into()); - let request = supergraph::Request::fake_builder() - .query(QUERY.to_string()) - .header("Apollo-Expose-Query-Plan", "true") - .build() - .expect("expecting valid request"); - - let response = supergraph_service - .oneshot(request) - .await - .unwrap() - .next_response() - .await - .unwrap(); - - insta::assert_json_snapshot!(response); -} - -#[tokio::test(flavor = "multi_thread")] -async fn test_type_conditions_enabled_shouldnt_make_article_fetch() { - let harness = setup_from_mocks( - json! {{ - "experimental_type_conditioned_fetching": true, - // will make debugging easier - "plugins": { - "experimental.expose_query_plan": true - }, - "include_subgraph_errors": { - "all": true - } - }}, - &[ - ( - "searchSubgraph", - include_str!("fixtures/type_conditions/search_no_articles.json"), - ), - ( - "artworkSubgraph", - include_str!("fixtures/type_conditions/artwork_no_articles.json"), - ), - ], - ); - let supergraph_service = harness.build_supergraph().await.unwrap(); - let mut variables = JsonMap::new(); - variables.insert("movieResultParam", "movieResultEnabled".into()); - variables.insert("articleResultParam", "articleResultEnabled".into()); - let request = supergraph::Request::fake_builder() - .query(QUERY.to_string()) - .header("Apollo-Expose-Query-Plan", "true") - .variables(variables) + .variables(Default::default()) .build() .expect("expecting valid request"); @@ -324,7 +81,7 @@ fn setup_from_mocks( mocked_subgraphs.insert(name, builder.build()); } - let schema = include_str!("fixtures/type_conditions/type_conditions.graphql"); + let schema = include_str!("fixtures/set_context/supergraph.graphql"); TestHarness::builder() .try_log_level("info") .configuration_json(configuration) @@ -333,98 +90,11 @@ fn setup_from_mocks( .extra_plugin(mocked_subgraphs) } -static QUERY: &str = r#" -query Search($movieResultParam: String, $articleResultParam: String) { - search { - ... on MovieResult { - sections { - ... on EntityCollectionSection { - id - title - artwork(params: $movieResultParam) - } - ... on GallerySection { - artwork(params: $movieResultParam) - id - } - } - id - } - ... on ArticleResult { - id - sections { - ... on GallerySection { - artwork(params: $articleResultParam) - } - ... on EntityCollectionSection { - artwork(params: $articleResultParam) - title - } - } - } - } -}"#; - -static QUERY_LIST_OF_LIST: &str = r#" -query Search($movieResultParam: String, $articleResultParam: String) { - searchListOfList { - ... on MovieResult { - sections { - ... on EntityCollectionSection { - id - title - artwork(params: $movieResultParam) - } - ... on GallerySection { - artwork(params: $movieResultParam) - id - } - } - id - } - ... on ArticleResult { - id - sections { - ... on GallerySection { - artwork(params: $articleResultParam) - } - ... on EntityCollectionSection { - artwork(params: $articleResultParam) - title - } - } - } - } -}"#; - -static QUERY_LIST_OF_LIST_OF_LIST: &str = r#" -query Search($movieResultParam: String, $articleResultParam: String) { - searchListOfListOfList { - ... on MovieResult { - sections { - ... on EntityCollectionSection { - id - title - artwork(params: $movieResultParam) - } - ... on GallerySection { - artwork(params: $movieResultParam) - id - } - } - id - } - ... on ArticleResult { - id - sections { - ... on GallerySection { - artwork(params: $articleResultParam) - } - ... on EntityCollectionSection { - artwork(params: $articleResultParam) - title - } - } +static QUERY: &str = r#"query Query { + t { + id + u { + field } } -}"#; + }"#; diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index 2ab4d92525..1cb6000639 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -20,7 +20,7 @@ reqwest = { workspace = true, features = ["json", "blocking"] } serde_json.workspace = true tokio.workspace = true # note: this dependency should _always_ be pinned, prefix the version with an `=` -router-bridge = "=0.5.20+v2.7.4" +router-bridge = { path = "../../federation-rs/federation-2/router-bridge" } # "=0.5.20+v2.7.4" [dev-dependencies] anyhow = "1" From a156efc0e79b4e450fa3e02144283f82bb6b3058 Mon Sep 17 00:00:00 2001 From: Chris Lenfest Date: Mon, 29 Apr 2024 20:58:57 -0500 Subject: [PATCH 03/69] checkpoint --- Cargo.lock | 2 - apollo-router/Cargo.toml | 2 +- apollo-router/src/query_planner/convert.rs | 3 + apollo-router/src/query_planner/execution.rs | 2 + apollo-router/src/query_planner/fetch.rs | 80 +++++++++---- apollo-router/src/query_planner/rewrites.rs | 9 +- ...ridge_query_planner__tests__plan_root.snap | 1 + ..._planner__tests__query_plan_from_json.snap | 5 + .../src/query_planner/subscription.rs | 1 + apollo-router/src/query_planner/tests.rs | 2 + .../set_context__set_context.snap.new | 113 ++++++++++++++++++ 11 files changed, 194 insertions(+), 26 deletions(-) create mode 100644 apollo-router/tests/snapshots/set_context__set_context.snap.new diff --git a/Cargo.lock b/Cargo.lock index acd4363f3c..5d8ec580eb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -221,8 +221,6 @@ dependencies = [ [[package]] name = "apollo-federation" version = "0.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9fc457f3e836a60ea3d4e1a25a8b42c5c62ddf13a2131c194d94f752c7a1475" dependencies = [ "apollo-compiler", "derive_more", diff --git a/apollo-router/Cargo.toml b/apollo-router/Cargo.toml index 1c5641a618..f284220fc9 100644 --- a/apollo-router/Cargo.toml +++ b/apollo-router/Cargo.toml @@ -65,7 +65,7 @@ askama = "0.12.1" access-json = "0.1.0" anyhow = "1.0.80" apollo-compiler.workspace = true -apollo-federation = "=0.0.11" +apollo-federation = { path = "../../federation-next" } arc-swap = "1.6.0" async-channel = "1.9.0" async-compression = { version = "0.4.6", features = [ diff --git a/apollo-router/src/query_planner/convert.rs b/apollo-router/src/query_planner/convert.rs index 79478bb120..181bb05ae5 100644 --- a/apollo-router/src/query_planner/convert.rs +++ b/apollo-router/src/query_planner/convert.rs @@ -66,6 +66,7 @@ impl From<&'_ next::FetchNode> for plan::PlanNode { operation_kind, input_rewrites, output_rewrites, + context_rewrites, } = value; Self::Fetch(super::fetch::FetchNode { service_name: subgraph_name.clone(), @@ -78,6 +79,7 @@ impl From<&'_ next::FetchNode> for plan::PlanNode { id: id.clone(), input_rewrites: option_vec(input_rewrites), output_rewrites: option_vec(output_rewrites), + context_rewrites: option_vec(context_rewrites), schema_aware_hash: Default::default(), authorization: Default::default(), }) @@ -145,6 +147,7 @@ impl From<&'_ next::FetchNode> for subscription::SubscriptionNode { operation_kind, input_rewrites, output_rewrites, + context_rewrites, } = value; Self { service_name: subgraph_name.clone(), diff --git a/apollo-router/src/query_planner/execution.rs b/apollo-router/src/query_planner/execution.rs index 4addf771a3..07af5fc86d 100644 --- a/apollo-router/src/query_planner/execution.rs +++ b/apollo-router/src/query_planner/execution.rs @@ -250,6 +250,8 @@ impl PlanNode { )) .await; value = v; + dbg!("fetch node response", ¤t_dir); + dbg!(&value); errors = e; } } diff --git a/apollo-router/src/query_planner/fetch.rs b/apollo-router/src/query_planner/fetch.rs index 2f6cb69a7d..7d80c5b361 100644 --- a/apollo-router/src/query_planner/fetch.rs +++ b/apollo-router/src/query_planner/fetch.rs @@ -126,6 +126,9 @@ pub(crate) struct FetchNode { // Optionally describes a number of "rewrites" to apply to the data that received from a fetch (and before it is applied to the current in-memory results). pub(crate) output_rewrites: Option>, + + // Optionally describes a number of "rewrites" to apply to the data that has already been received further up the tree + pub(crate) context_rewrites: Option>, // hash for the query and relevant parts of the schema. if two different schemas provide the exact same types, fields and directives // affecting the query, then they will have the same hash @@ -248,6 +251,27 @@ pub(crate) struct Variables { pub(crate) inverted_paths: Vec>, } +fn path_to_data( + data: &Value, + path: &Vec +) -> Option { + // TODO: We will have fragments with type conditions that need to be dealt with, but it's not working just yet + let v = match &path[0] { + PathElement::Fragment(s) => data.get(s), + PathElement::Key(v, t) => data.get(v), + PathElement::Flatten(_) => None, + PathElement::Index(_) => None, + }; + + if path.len() > 1 { + if let Some(val) = v { + let remaining_path = path.iter().skip(1).cloned().collect(); + return path_to_data(val, &remaining_path); + } + } + v.cloned() +} + impl Variables { #[instrument(skip_all, level = "debug", name = "make_variables")] #[allow(clippy::too_many_arguments)] @@ -259,31 +283,40 @@ impl Variables { request: &Arc>, schema: &Schema, input_rewrites: &Option>, + context_rewrites: &Option>, ) -> Option { - let input_rewrites = if let Some(ir) = input_rewrites { - Some( - ir.into_iter() - .map(|item| { - if let DataRewrite::KeyRenamer(dr) = item { - if dr.path == Path(vec![PathElement::Key("prop".to_string(), None)]) { - return DataRewrite::KeyRenamer({ - DataKeyRenamer { - path: Path(vec![ - PathElement::Key("t".to_string(), None), - PathElement::Key("prop".to_string(), None), - ]), - rename_key_to: dr.rename_key_to.clone() - } - }); + match context_rewrites { + Some(crw) => { + crw.iter().for_each(|rewrite| { + if let DataRewrite::KeyRenamer(item) = rewrite { + let up_count = item.path.iter().enumerate().find_map(|(index, p)| match p { + PathElement::Key(key, _) => if key != ".." { Some(index) } else { None }, + _ => Some(index), + }); + + dbg!(¤t_dir); + if let Some(count) = up_count { + let mut data_path: Vec = current_dir.iter().take(current_dir.len() - count).map(|e| e.clone()).collect(); + item.path.iter().skip(count).for_each(|elem| data_path.push(elem.clone())); + dbg!(&data_path); + dbg!(&data); + let value = path_to_data(data, &data_path); + let dp: Path = Path(data_path.into_iter().collect()); + let rewrite_with_updated_path = DataRewrite::KeyRenamer({ + DataKeyRenamer { + path: dp, + rename_key_to: item.rename_key_to.clone(), + } + }); + if let Some(mut v) = value { + rewrites::apply_single_rewrite(schema, &mut v, &rewrite_with_updated_path); } } - item.clone() - }) - .collect(), - ) - } else { - input_rewrites.clone() - }; + } + }); + } + None => {} + } dbg!(&input_rewrites); let body = request.body(); @@ -300,7 +333,9 @@ impl Variables { let mut values: IndexSet = IndexSet::new(); data.select_values_and_paths(schema, current_dir, |path, value| { + dbg!(&requires); let mut value = execute_selection_set(value, requires, schema, None); + dbg!(&value); if value.as_object().map(|o| !o.is_empty()).unwrap_or(false) { rewrites::apply_rewrites(schema, &mut value, &input_rewrites); match values.get_index_of(&value) { @@ -393,6 +428,7 @@ impl FetchNode { parameters.supergraph_request, parameters.schema, &self.input_rewrites, + &self.context_rewrites, ) { Some(variables) => variables, None => { diff --git a/apollo-router/src/query_planner/rewrites.rs b/apollo-router/src/query_planner/rewrites.rs index 62497f2c5d..9c3dd2ed22 100644 --- a/apollo-router/src/query_planner/rewrites.rs +++ b/apollo-router/src/query_planner/rewrites.rs @@ -58,7 +58,6 @@ impl DataRewrite { if let Some((parent, PathElement::Key(k, _))) = split_path_last_element(&setter.path) { - tracing::info!("REWRITES 1 {} {}", k, parent); data.select_values_and_paths_mut(schema, &parent, |_path, obj| { if let Some(value) = obj.get_mut(k) { *value = setter.set_value_to.clone() @@ -108,6 +107,14 @@ pub(crate) fn apply_rewrites( } } +pub(crate) fn apply_single_rewrite( + schema: &Schema, + value: &mut Value, + rewrite: &DataRewrite, +) { + rewrite.maybe_apply(schema, value); +} + #[cfg(test)] mod tests { use serde_json_bytes::json; diff --git a/apollo-router/src/query_planner/snapshots/apollo_router__query_planner__bridge_query_planner__tests__plan_root.snap b/apollo-router/src/query_planner/snapshots/apollo_router__query_planner__bridge_query_planner__tests__plan_root.snap index 5e07a0cfd1..2f6c55f216 100644 --- a/apollo-router/src/query_planner/snapshots/apollo_router__query_planner__bridge_query_planner__tests__plan_root.snap +++ b/apollo-router/src/query_planner/snapshots/apollo_router__query_planner__bridge_query_planner__tests__plan_root.snap @@ -13,6 +13,7 @@ Fetch( id: None, input_rewrites: None, output_rewrites: None, + context_rewrites: None, schema_aware_hash: QueryHash( "68a86d7602ea2876e77b84d7942f585ef6b6101887bb2979d1f0af3b28c9a0ed", ), diff --git a/apollo-router/src/query_planner/snapshots/apollo_router__query_planner__tests__query_plan_from_json.snap b/apollo-router/src/query_planner/snapshots/apollo_router__query_planner__tests__query_plan_from_json.snap index ef8a64f2a6..c18018d7a2 100644 --- a/apollo-router/src/query_planner/snapshots/apollo_router__query_planner__tests__query_plan_from_json.snap +++ b/apollo-router/src/query_planner/snapshots/apollo_router__query_planner__tests__query_plan_from_json.snap @@ -17,6 +17,7 @@ Sequence { id: None, input_rewrites: None, output_rewrites: None, + context_rewrites: None, schema_aware_hash: QueryHash( "", ), @@ -81,6 +82,7 @@ Sequence { id: None, input_rewrites: None, output_rewrites: None, + context_rewrites: None, schema_aware_hash: QueryHash( "", ), @@ -155,6 +157,7 @@ Sequence { id: None, input_rewrites: None, output_rewrites: None, + context_rewrites: None, schema_aware_hash: QueryHash( "", ), @@ -216,6 +219,7 @@ Sequence { id: None, input_rewrites: None, output_rewrites: None, + context_rewrites: None, schema_aware_hash: QueryHash( "", ), @@ -287,6 +291,7 @@ Sequence { id: None, input_rewrites: None, output_rewrites: None, + context_rewrites: None, schema_aware_hash: QueryHash( "", ), diff --git a/apollo-router/src/query_planner/subscription.rs b/apollo-router/src/query_planner/subscription.rs index caf90730e6..89db823b44 100644 --- a/apollo-router/src/query_planner/subscription.rs +++ b/apollo-router/src/query_planner/subscription.rs @@ -208,6 +208,7 @@ impl SubscriptionNode { parameters.supergraph_request, parameters.schema, &self.input_rewrites, + &self.input_rewrites, // TODO: Fix me ) { Some(variables) => variables, None => { diff --git a/apollo-router/src/query_planner/tests.rs b/apollo-router/src/query_planner/tests.rs index b1d007a216..35b9d71972 100644 --- a/apollo-router/src/query_planner/tests.rs +++ b/apollo-router/src/query_planner/tests.rs @@ -266,6 +266,7 @@ async fn defer() { id: Some("fetch1".into()), input_rewrites: None, output_rewrites: None, + context_rewrites: None, schema_aware_hash: Default::default(), authorization: Default::default(), }))), @@ -311,6 +312,7 @@ async fn defer() { id: Some("fetch2".into()), input_rewrites: None, output_rewrites: None, + context_rewrites: None, schema_aware_hash: Default::default(), authorization: Default::default(), })), diff --git a/apollo-router/tests/snapshots/set_context__set_context.snap.new b/apollo-router/tests/snapshots/set_context__set_context.snap.new new file mode 100644 index 0000000000..97907560a3 --- /dev/null +++ b/apollo-router/tests/snapshots/set_context__set_context.snap.new @@ -0,0 +1,113 @@ +--- +source: apollo-router/tests/set_context.rs +assertion_line: 60 +expression: response +--- +{ + "data": null, + "extensions": { + "valueCompletion": [ + { + "message": "Cannot return null for non-nullable field U.field", + "path": [ + "t", + "u" + ] + }, + { + "message": "Cannot return null for non-nullable field T.u", + "path": [ + "t", + "u" + ] + }, + { + "message": "Cannot return null for non-nullable field T!.t", + "path": [ + "t" + ] + } + ], + "apolloQueryPlan": { + "object": { + "kind": "QueryPlan", + "node": { + "kind": "Sequence", + "nodes": [ + { + "kind": "Fetch", + "serviceName": "one", + "variableUsages": [], + "operation": "query Query__one__0{t{prop id u{id}}}", + "operationName": "Query__one__0", + "operationKind": "query", + "id": null, + "inputRewrites": null, + "outputRewrites": null, + "contextRewrites": null, + "schemaAwareHash": "0c11d75a9caa25f012a22034f3deff983b512276d0405e155bd83a459d803b72", + "authorization": { + "is_authenticated": false, + "scopes": [], + "policies": [] + } + }, + { + "kind": "Flatten", + "path": [ + "t", + "u" + ], + "node": { + "kind": "Fetch", + "serviceName": "one", + "requires": [ + { + "kind": "InlineFragment", + "typeCondition": "U", + "selections": [ + { + "kind": "Field", + "name": "__typename" + }, + { + "kind": "Field", + "name": "id" + } + ] + } + ], + "variableUsages": [ + "one_U_field_a" + ], + "operation": "query Query__one__1($representations:[_Any!]!$one_U_field_a:String!){_entities(representations:$representations){...on U{id field(a:$one_U_field_a)}}}", + "operationName": "Query__one__1", + "operationKind": "query", + "id": null, + "inputRewrites": null, + "outputRewrites": null, + "contextRewrites": [ + { + "kind": "KeyRenamer", + "path": [ + "..", + "prop" + ], + "renameKeyTo": "one_U_field_a" + } + ], + "schemaAwareHash": "2116d92c6c7512fbed7dc7b4fe41a49ceaf01df2362bb49c025b6316230cce74", + "authorization": { + "is_authenticated": false, + "scopes": [], + "policies": [] + } + } + } + ] + } + }, + "text": "QueryPlan {\n Sequence {\n Fetch(service: \"one\") {\n {\n t {\n prop\n id\n u {\n id\n }\n }\n }\n },\n Flatten(path: \"t.u\") {\n Fetch(service: \"one\") {\n {\n ... on U {\n __typename\n id\n }\n } =>\n {\n ... on U {\n id\n field(a: $one_U_field_a)\n }\n }\n },\n },\n },\n}" + } + } +} From 2ac6b46cd855f7995d52a66f7f4492e3724caa2f Mon Sep 17 00:00:00 2001 From: o0Ignition0o Date: Tue, 30 Apr 2024 11:10:29 +0200 Subject: [PATCH 04/69] it s not working yet but contextRewrites makes it to the router --- Cargo.lock | 12 ++-- apollo-router/src/query_planner/execution.rs | 2 - apollo-router/src/query_planner/fetch.rs | 55 +++++++++---------- ...snap.new => set_context__set_context.snap} | 1 - 4 files changed, 33 insertions(+), 37 deletions(-) rename apollo-router/tests/snapshots/{set_context__set_context.snap.new => set_context__set_context.snap} (99%) diff --git a/Cargo.lock b/Cargo.lock index 5d8ec580eb..85dbdc88b0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6073,9 +6073,9 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.197" +version = "1.0.199" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" +checksum = "0c9f6e76df036c77cd94996771fb40db98187f096dd0b9af39c6c6e452ba966a" dependencies = [ "serde_derive", ] @@ -6091,9 +6091,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.197" +version = "1.0.199" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" +checksum = "11bd257a6541e141e42ca6d24ae26f7714887b47e89aa739099104c7e4d3b7fc" dependencies = [ "proc-macro2 1.0.76", "quote 1.0.35", @@ -6125,9 +6125,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.114" +version = "1.0.116" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" +checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813" dependencies = [ "indexmap 2.2.3", "itoa", diff --git a/apollo-router/src/query_planner/execution.rs b/apollo-router/src/query_planner/execution.rs index 07af5fc86d..4addf771a3 100644 --- a/apollo-router/src/query_planner/execution.rs +++ b/apollo-router/src/query_planner/execution.rs @@ -250,8 +250,6 @@ impl PlanNode { )) .await; value = v; - dbg!("fetch node response", ¤t_dir); - dbg!(&value); errors = e; } } diff --git a/apollo-router/src/query_planner/fetch.rs b/apollo-router/src/query_planner/fetch.rs index 7d80c5b361..f9a26e1137 100644 --- a/apollo-router/src/query_planner/fetch.rs +++ b/apollo-router/src/query_planner/fetch.rs @@ -285,37 +285,36 @@ impl Variables { input_rewrites: &Option>, context_rewrites: &Option>, ) -> Option { - match context_rewrites { - Some(crw) => { - crw.iter().for_each(|rewrite| { - if let DataRewrite::KeyRenamer(item) = rewrite { - let up_count = item.path.iter().enumerate().find_map(|(index, p)| match p { - PathElement::Key(key, _) => if key != ".." { Some(index) } else { None }, - _ => Some(index), - }); - - dbg!(¤t_dir); - if let Some(count) = up_count { - let mut data_path: Vec = current_dir.iter().take(current_dir.len() - count).map(|e| e.clone()).collect(); - item.path.iter().skip(count).for_each(|elem| data_path.push(elem.clone())); - dbg!(&data_path); - dbg!(&data); - let value = path_to_data(data, &data_path); - let dp: Path = Path(data_path.into_iter().collect()); - let rewrite_with_updated_path = DataRewrite::KeyRenamer({ - DataKeyRenamer { - path: dp, - rename_key_to: item.rename_key_to.clone(), - } - }); - if let Some(mut v) = value { - rewrites::apply_single_rewrite(schema, &mut v, &rewrite_with_updated_path); + // apply context_rewrites + dbg!(&context_rewrites); + if let Some(crw) = context_rewrites { + crw.iter().for_each(|rewrite| { + if let DataRewrite::KeyRenamer(item) = rewrite { + let up_count = item.path.iter().enumerate().find_map(|(index, p)| match p { + PathElement::Key(key, _) => if key != ".." { Some(index) } else { None }, + _ => Some(index), + }); + + dbg!(¤t_dir); + if let Some(count) = up_count { + let mut data_path: Vec = current_dir.iter().take(current_dir.len() - count).map(|e| e.clone()).collect(); + item.path.iter().skip(count).for_each(|elem| data_path.push(elem.clone())); + dbg!(&data_path); + dbg!(&data); + let value = path_to_data(data, &data_path); + let dp: Path = Path(data_path.into_iter().collect()); + let rewrite_with_updated_path = DataRewrite::KeyRenamer({ + DataKeyRenamer { + path: dp, + rename_key_to: item.rename_key_to.clone(), } + }); + if let Some(mut v) = value { + rewrites::apply_single_rewrite(schema, &mut v, &rewrite_with_updated_path); } } - }); - } - None => {} + } + }); } dbg!(&input_rewrites); diff --git a/apollo-router/tests/snapshots/set_context__set_context.snap.new b/apollo-router/tests/snapshots/set_context__set_context.snap similarity index 99% rename from apollo-router/tests/snapshots/set_context__set_context.snap.new rename to apollo-router/tests/snapshots/set_context__set_context.snap index 97907560a3..79a01ca763 100644 --- a/apollo-router/tests/snapshots/set_context__set_context.snap.new +++ b/apollo-router/tests/snapshots/set_context__set_context.snap @@ -1,6 +1,5 @@ --- source: apollo-router/tests/set_context.rs -assertion_line: 60 expression: response --- { From 631e1af9bd7ec388c5b6b5132db9ed2ae5d2210e Mon Sep 17 00:00:00 2001 From: o0Ignition0o Date: Tue, 30 Apr 2024 12:30:59 +0200 Subject: [PATCH 05/69] ok context makes it to variables. --- apollo-router/src/query_planner/fetch.rs | 78 ++++++++++++++------- apollo-router/src/query_planner/rewrites.rs | 6 +- 2 files changed, 52 insertions(+), 32 deletions(-) diff --git a/apollo-router/src/query_planner/fetch.rs b/apollo-router/src/query_planner/fetch.rs index f9a26e1137..ffa54ffb39 100644 --- a/apollo-router/src/query_planner/fetch.rs +++ b/apollo-router/src/query_planner/fetch.rs @@ -126,7 +126,7 @@ pub(crate) struct FetchNode { // Optionally describes a number of "rewrites" to apply to the data that received from a fetch (and before it is applied to the current in-memory results). pub(crate) output_rewrites: Option>, - + // Optionally describes a number of "rewrites" to apply to the data that has already been received further up the tree pub(crate) context_rewrites: Option>, @@ -251,10 +251,7 @@ pub(crate) struct Variables { pub(crate) inverted_paths: Vec>, } -fn path_to_data( - data: &Value, - path: &Vec -) -> Option { +fn data_at_path<'v>(data: &'v Value, path: &Vec) -> Option<&'v Value> { // TODO: We will have fragments with type conditions that need to be dealt with, but it's not working just yet let v = match &path[0] { PathElement::Fragment(s) => data.get(s), @@ -266,10 +263,10 @@ fn path_to_data( if path.len() > 1 { if let Some(val) = v { let remaining_path = path.iter().skip(1).cloned().collect(); - return path_to_data(val, &remaining_path); + return data_at_path(val, &remaining_path); } } - v.cloned() + v } impl Variables { @@ -285,23 +282,39 @@ impl Variables { input_rewrites: &Option>, context_rewrites: &Option>, ) -> Option { + // TODO: remove these clones we're in the hot path + let mut input_rewrites = input_rewrites.clone().unwrap_or_default(); + let mut context_variables: HashMap = Default::default(); // apply context_rewrites dbg!(&context_rewrites); if let Some(crw) = context_rewrites { crw.iter().for_each(|rewrite| { if let DataRewrite::KeyRenamer(item) = rewrite { - let up_count = item.path.iter().enumerate().find_map(|(index, p)| match p { - PathElement::Key(key, _) => if key != ".." { Some(index) } else { None }, + let up_count = item.path.iter().enumerate().find_map(|(index, p)| match p { + PathElement::Key(key, _) => { + if key != ".." { + Some(index) + } else { + None + } + } _ => Some(index), }); - dbg!(¤t_dir); + // dbg!(¤t_dir); if let Some(count) = up_count { - let mut data_path: Vec = current_dir.iter().take(current_dir.len() - count).map(|e| e.clone()).collect(); - item.path.iter().skip(count).for_each(|elem| data_path.push(elem.clone())); - dbg!(&data_path); - dbg!(&data); - let value = path_to_data(data, &data_path); + let mut data_path: Vec = current_dir + .iter() + .take(current_dir.len() - count) + .map(|e| e.clone()) + .collect(); + item.path + .iter() + .skip(count) + .for_each(|elem| data_path.push(elem.clone())); + let value = data_at_path(data, &data_path); + // dbg!(&data_path); + // dbg!(&data); let dp: Path = Path(data_path.into_iter().collect()); let rewrite_with_updated_path = DataRewrite::KeyRenamer({ DataKeyRenamer { @@ -309,25 +322,36 @@ impl Variables { rename_key_to: item.rename_key_to.clone(), } }); - if let Some(mut v) = value { - rewrites::apply_single_rewrite(schema, &mut v, &rewrite_with_updated_path); + + if let Some(v) = value { + // TODO: not great + let mut new_value = v.clone(); + rewrites::apply_single_rewrite( + schema, + &mut new_value, + &rewrite_with_updated_path, + ); + context_variables.insert(item.rename_key_to.clone(), new_value); } } } }); } - dbg!(&input_rewrites); + let input_rewrites = &Some(input_rewrites); let body = request.body(); - if !requires.is_empty() { - let mut variables = Object::with_capacity(1 + variable_usages.len()); - - variables.extend(variable_usages.iter().filter_map(|key| { - body.variables - .get_key_value(key.as_str()) - .map(|(variable_key, value)| (variable_key.clone(), value.clone())) - })); + let mut variables: serde_json_bytes::Map = + Object::with_capacity(1 + variable_usages.len()); + variables.extend(context_variables.iter().map(|(key, value)| { + (key.as_str().into(), value.clone()) + })); + variables.extend(variable_usages.iter().filter_map(|key| { + body.variables + .get_key_value(key.as_str()) + .map(|(variable_key, value)| (variable_key.clone(), value.clone())) + })); + if !requires.is_empty() { let mut inverted_paths: Vec> = Vec::new(); let mut values: IndexSet = IndexSet::new(); @@ -380,7 +404,7 @@ impl Variables { variables: variable_usages .iter() .filter_map(|key| { - body.variables + variables .get_key_value(key.as_str()) .map(|(variable_key, value)| (variable_key.clone(), value.clone())) }) diff --git a/apollo-router/src/query_planner/rewrites.rs b/apollo-router/src/query_planner/rewrites.rs index 9c3dd2ed22..b90c487217 100644 --- a/apollo-router/src/query_planner/rewrites.rs +++ b/apollo-router/src/query_planner/rewrites.rs @@ -107,11 +107,7 @@ pub(crate) fn apply_rewrites( } } -pub(crate) fn apply_single_rewrite( - schema: &Schema, - value: &mut Value, - rewrite: &DataRewrite, -) { +pub(crate) fn apply_single_rewrite(schema: &Schema, value: &mut Value, rewrite: &DataRewrite) { rewrite.maybe_apply(schema, value); } From 0a50a26b3562a97897a44b411e5a1d72a90f3ad2 Mon Sep 17 00:00:00 2001 From: o0Ignition0o Date: Tue, 30 Apr 2024 12:55:22 +0200 Subject: [PATCH 06/69] ok we have a test now --- apollo-router/src/query_planner/fetch.rs | 8 +- apollo-router/src/spec/query.rs | 1 + .../tests/fixtures/set_context/one.json | 35 ++++--- .../fixtures/set_context/supergraph.graphql | 92 +++++++++++++------ apollo-router/tests/set_context.rs | 7 +- .../snapshots/set_context__set_context.snap | 55 +++++------ 6 files changed, 112 insertions(+), 86 deletions(-) diff --git a/apollo-router/src/query_planner/fetch.rs b/apollo-router/src/query_planner/fetch.rs index ffa54ffb39..eb5ced8189 100644 --- a/apollo-router/src/query_planner/fetch.rs +++ b/apollo-router/src/query_planner/fetch.rs @@ -282,11 +282,8 @@ impl Variables { input_rewrites: &Option>, context_rewrites: &Option>, ) -> Option { - // TODO: remove these clones we're in the hot path - let mut input_rewrites = input_rewrites.clone().unwrap_or_default(); let mut context_variables: HashMap = Default::default(); // apply context_rewrites - dbg!(&context_rewrites); if let Some(crw) = context_rewrites { crw.iter().for_each(|rewrite| { if let DataRewrite::KeyRenamer(item) = rewrite { @@ -338,7 +335,6 @@ impl Variables { }); } - let input_rewrites = &Some(input_rewrites); let body = request.body(); let mut variables: serde_json_bytes::Map = Object::with_capacity(1 + variable_usages.len()); @@ -356,11 +352,9 @@ impl Variables { let mut values: IndexSet = IndexSet::new(); data.select_values_and_paths(schema, current_dir, |path, value| { - dbg!(&requires); let mut value = execute_selection_set(value, requires, schema, None); - dbg!(&value); if value.as_object().map(|o| !o.is_empty()).unwrap_or(false) { - rewrites::apply_rewrites(schema, &mut value, &input_rewrites); + rewrites::apply_rewrites(schema, &mut value, input_rewrites); match values.get_index_of(&value) { Some(index) => { inverted_paths[index].push(path.clone()); diff --git a/apollo-router/src/spec/query.rs b/apollo-router/src/spec/query.rs index 68fe3a94e4..aeabaaf449 100644 --- a/apollo-router/src/spec/query.rs +++ b/apollo-router/src/spec/query.rs @@ -793,6 +793,7 @@ impl Query { output: &mut Object, path: &mut Vec>, ) -> Result<(), InvalidValue> { + dbg!(&selection_set, &input, &output, &path); for selection in selection_set { match selection { Selection::Field { diff --git a/apollo-router/tests/fixtures/set_context/one.json b/apollo-router/tests/fixtures/set_context/one.json index f753811bb3..eac4c06681 100644 --- a/apollo-router/tests/fixtures/set_context/one.json +++ b/apollo-router/tests/fixtures/set_context/one.json @@ -2,15 +2,17 @@ "mocks": [ { "request": { - "query": "query Query__one__0{t{prop id u{id}}}", - "operationName": "Query__one__0" + "query": "query Query__Subgraph1__0{t{__typename prop id u{__typename id}}}", + "operationName": "Query__Subgraph1__0" }, "response": { "data": { "t": { + "__typename": "T", "prop": "prop value", "id": "1", "u": { + "__typename": "U", "id": "1" } } @@ -18,22 +20,25 @@ } }, { - "request": { - "query": "query Query__one__0{t{prop id u{__typename id}}}", - "operationName": "Query__one__0" - }, - "response": { - "data": { - "t": { - "prop": "prop value", + "request": { + "query": "query Query__Subgraph1__1($representations:[_Any!]!$Subgraph1_U_field_a:String!){_entities(representations:$representations){...on U{__typename id field(a:$Subgraph1_U_field_a)}}}", + "operationName": "Query__Subgraph1__1", + "variables": { + "Subgraph1_U_field_a": "prop value", + "representations": [{ "__typename": "U", "id": "1" }] + } + }, + "response": { + "data": { + "_entities": [ + { + "__typename": "U", "id": "1", - "u": { - "__typename": "U", - "id": "1" - } + "field": 1234 } - } + ] } } + } ] } diff --git a/apollo-router/tests/fixtures/set_context/supergraph.graphql b/apollo-router/tests/fixtures/set_context/supergraph.graphql index f2a4455ccc..77ba17cf44 100644 --- a/apollo-router/tests/fixtures/set_context/supergraph.graphql +++ b/apollo-router/tests/fixtures/set_context/supergraph.graphql @@ -1,8 +1,7 @@ schema @link(url: "https://specs.apollo.dev/link/v1.0") @link(url: "https://specs.apollo.dev/join/v0.4", for: EXECUTION) - @link(url: "https://specs.apollo.dev/context/v0.1", for: SECURITY) -{ + @link(url: "https://specs.apollo.dev/context/v0.1", for: SECURITY) { query: Query } @@ -10,21 +9,52 @@ directive @context(name: String!) repeatable on INTERFACE | OBJECT | UNION directive @context__fromContext(field: String) on ARGUMENT_DEFINITION -directive @join__directive(graphs: [join__Graph!], name: String!, args: join__DirectiveArguments) repeatable on SCHEMA | OBJECT | INTERFACE | FIELD_DEFINITION +directive @join__directive( + graphs: [join__Graph!] + name: String! + args: join__DirectiveArguments +) repeatable on SCHEMA | OBJECT | INTERFACE | FIELD_DEFINITION directive @join__enumValue(graph: join__Graph!) repeatable on ENUM_VALUE -directive @join__field(graph: join__Graph, requires: join__FieldSet, provides: join__FieldSet, type: String, external: Boolean, override: String, usedOverridden: Boolean, overrideLabel: String, contextArguments: [join__ContextArgument!]) repeatable on FIELD_DEFINITION | INPUT_FIELD_DEFINITION +directive @join__field( + graph: join__Graph + requires: join__FieldSet + provides: join__FieldSet + type: String + external: Boolean + override: String + usedOverridden: Boolean + overrideLabel: String + contextArguments: [join__ContextArgument!] +) repeatable on FIELD_DEFINITION | INPUT_FIELD_DEFINITION directive @join__graph(name: String!, url: String!) on ENUM_VALUE -directive @join__implements(graph: join__Graph!, interface: String!) repeatable on OBJECT | INTERFACE - -directive @join__type(graph: join__Graph!, key: join__FieldSet, extension: Boolean! = false, resolvable: Boolean! = true, isInterfaceObject: Boolean! = false) repeatable on OBJECT | INTERFACE | UNION | ENUM | INPUT_OBJECT | SCALAR - -directive @join__unionMember(graph: join__Graph!, member: String!) repeatable on UNION - -directive @link(url: String, as: String, for: link__Purpose, import: [link__Import]) repeatable on SCHEMA +directive @join__implements( + graph: join__Graph! + interface: String! +) repeatable on OBJECT | INTERFACE + +directive @join__type( + graph: join__Graph! + key: join__FieldSet + extension: Boolean! = false + resolvable: Boolean! = true + isInterfaceObject: Boolean! = false +) repeatable on OBJECT | INTERFACE | UNION | ENUM | INPUT_OBJECT | SCALAR + +directive @join__unionMember( + graph: join__Graph! + member: String! +) repeatable on UNION + +directive @link( + url: String + as: String + for: link__Purpose + import: [link__Import] +) repeatable on SCHEMA scalar context__context @@ -42,8 +72,8 @@ scalar join__FieldSet scalar join__FieldValue enum join__Graph { - ONE @join__graph(name: "one", url: "http://localhost:4001") - TWO @join__graph(name: "two", url: "http://localhost:4002") + SUBGRAPH1 @join__graph(name: "Subgraph1", url: "https://Subgraph1") + SUBGRAPH2 @join__graph(name: "Subgraph2", url: "https://Subgraph2") } scalar link__Import @@ -60,28 +90,34 @@ enum link__Purpose { EXECUTION } -type Query - @join__type(graph: ONE) - @join__type(graph: TWO) -{ - t: T! @join__field(graph: ONE) - a: Int! @join__field(graph: TWO) +type Query @join__type(graph: SUBGRAPH1) @join__type(graph: SUBGRAPH2) { + t: T! @join__field(graph: SUBGRAPH1) + a: Int! @join__field(graph: SUBGRAPH2) } type T - @join__type(graph: ONE, key: "id") - @context(name: "one__context") -{ + @join__type(graph: SUBGRAPH1, key: "id") + @context(name: "Subgraph1__context") { id: ID! u: U! prop: String! } type U - @join__type(graph: ONE, key: "id") - @join__type(graph: TWO, key: "id") -{ + @join__type(graph: SUBGRAPH1, key: "id") + @join__type(graph: SUBGRAPH2, key: "id") { id: ID! - field: Int! @join__field(graph: ONE, contextArguments: [{context: "one__context", name: "a", type: "String!", selection: "{ prop }"}]) - k: String! @join__field(graph: TWO) -} \ No newline at end of file + b: String! @join__field(graph: SUBGRAPH1) + field: Int! + @join__field( + graph: SUBGRAPH1 + contextArguments: [ + { + context: "Subgraph1__context" + name: "a" + type: "String!" + selection: "{ prop }" + } + ] + ) +} diff --git a/apollo-router/tests/set_context.rs b/apollo-router/tests/set_context.rs index 566e94741e..11988d05e7 100644 --- a/apollo-router/tests/set_context.rs +++ b/apollo-router/tests/set_context.rs @@ -37,8 +37,8 @@ async fn test_set_context() { } }}, &[ - ("one", include_str!("fixtures/set_context/one.json")), - ("two", include_str!("fixtures/set_context/two.json")), + ("Subgraph1", include_str!("fixtures/set_context/one.json")), + ("Subgraph2", include_str!("fixtures/set_context/two.json")), ], ); let supergraph_service = harness.build_supergraph().await.unwrap(); @@ -90,10 +90,13 @@ fn setup_from_mocks( .extra_plugin(mocked_subgraphs) } +// TODO[clenfest]: figure out why i need __typename here? static QUERY: &str = r#"query Query { t { + __typename id u { + __typename field } } diff --git a/apollo-router/tests/snapshots/set_context__set_context.snap b/apollo-router/tests/snapshots/set_context__set_context.snap index 79a01ca763..4251e6580a 100644 --- a/apollo-router/tests/snapshots/set_context__set_context.snap +++ b/apollo-router/tests/snapshots/set_context__set_context.snap @@ -3,30 +3,17 @@ source: apollo-router/tests/set_context.rs expression: response --- { - "data": null, - "extensions": { - "valueCompletion": [ - { - "message": "Cannot return null for non-nullable field U.field", - "path": [ - "t", - "u" - ] - }, - { - "message": "Cannot return null for non-nullable field T.u", - "path": [ - "t", - "u" - ] - }, - { - "message": "Cannot return null for non-nullable field T!.t", - "path": [ - "t" - ] + "data": { + "t": { + "__typename": "T", + "id": "1", + "u": { + "__typename": "U", + "field": 1234 } - ], + } + }, + "extensions": { "apolloQueryPlan": { "object": { "kind": "QueryPlan", @@ -35,16 +22,16 @@ expression: response "nodes": [ { "kind": "Fetch", - "serviceName": "one", + "serviceName": "Subgraph1", "variableUsages": [], - "operation": "query Query__one__0{t{prop id u{id}}}", - "operationName": "Query__one__0", + "operation": "query Query__Subgraph1__0{t{__typename prop id u{__typename id}}}", + "operationName": "Query__Subgraph1__0", "operationKind": "query", "id": null, "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "0c11d75a9caa25f012a22034f3deff983b512276d0405e155bd83a459d803b72", + "schemaAwareHash": "2a8f649a16b50733cc9918606128e13ea8e101604969209cc0df7e0306121118", "authorization": { "is_authenticated": false, "scopes": [], @@ -59,7 +46,7 @@ expression: response ], "node": { "kind": "Fetch", - "serviceName": "one", + "serviceName": "Subgraph1", "requires": [ { "kind": "InlineFragment", @@ -77,10 +64,10 @@ expression: response } ], "variableUsages": [ - "one_U_field_a" + "Subgraph1_U_field_a" ], - "operation": "query Query__one__1($representations:[_Any!]!$one_U_field_a:String!){_entities(representations:$representations){...on U{id field(a:$one_U_field_a)}}}", - "operationName": "Query__one__1", + "operation": "query Query__Subgraph1__1($representations:[_Any!]!$Subgraph1_U_field_a:String!){_entities(representations:$representations){...on U{__typename id field(a:$Subgraph1_U_field_a)}}}", + "operationName": "Query__Subgraph1__1", "operationKind": "query", "id": null, "inputRewrites": null, @@ -92,10 +79,10 @@ expression: response "..", "prop" ], - "renameKeyTo": "one_U_field_a" + "renameKeyTo": "Subgraph1_U_field_a" } ], - "schemaAwareHash": "2116d92c6c7512fbed7dc7b4fe41a49ceaf01df2362bb49c025b6316230cce74", + "schemaAwareHash": "cbf8f9ce0545296f3305797769f5761b58367c4b25608467fc335c9d62df8cb1", "authorization": { "is_authenticated": false, "scopes": [], @@ -106,7 +93,7 @@ expression: response ] } }, - "text": "QueryPlan {\n Sequence {\n Fetch(service: \"one\") {\n {\n t {\n prop\n id\n u {\n id\n }\n }\n }\n },\n Flatten(path: \"t.u\") {\n Fetch(service: \"one\") {\n {\n ... on U {\n __typename\n id\n }\n } =>\n {\n ... on U {\n id\n field(a: $one_U_field_a)\n }\n }\n },\n },\n },\n}" + "text": "QueryPlan {\n Sequence {\n Fetch(service: \"Subgraph1\") {\n {\n t {\n __typename\n prop\n id\n u {\n __typename\n id\n }\n }\n }\n },\n Flatten(path: \"t.u\") {\n Fetch(service: \"Subgraph1\") {\n {\n ... on U {\n __typename\n id\n }\n } =>\n {\n ... on U {\n __typename\n id\n field(a: $Subgraph1_U_field_a)\n }\n }\n },\n },\n },\n}" } } } From c97e43c15c5b967e804e8f919c547726988967a4 Mon Sep 17 00:00:00 2001 From: o0Ignition0o Date: Tue, 30 Apr 2024 14:45:56 +0200 Subject: [PATCH 07/69] wip --- .../tests/fixtures/set_context/one.json | 17 +++ apollo-router/tests/set_context.rs | 47 ++++++++ ...set_context__set_context_no_typenames.snap | 112 ++++++++++++++++++ 3 files changed, 176 insertions(+) create mode 100644 apollo-router/tests/snapshots/set_context__set_context_no_typenames.snap diff --git a/apollo-router/tests/fixtures/set_context/one.json b/apollo-router/tests/fixtures/set_context/one.json index eac4c06681..62fc98b9eb 100644 --- a/apollo-router/tests/fixtures/set_context/one.json +++ b/apollo-router/tests/fixtures/set_context/one.json @@ -19,6 +19,23 @@ } } }, + { + "request": { + "query": "query Query__Subgraph1__0{t{prop id u{id}}}", + "operationName": "Query__Subgraph1__0" + }, + "response": { + "data": { + "t": { + "prop": "prop value", + "id": "1", + "u": { + "id": "1" + } + } + } + } + }, { "request": { "query": "query Query__Subgraph1__1($representations:[_Any!]!$Subgraph1_U_field_a:String!){_entities(representations:$representations){...on U{__typename id field(a:$Subgraph1_U_field_a)}}}", diff --git a/apollo-router/tests/set_context.rs b/apollo-router/tests/set_context.rs index 11988d05e7..0a79cdb0b9 100644 --- a/apollo-router/tests/set_context.rs +++ b/apollo-router/tests/set_context.rs @@ -60,6 +60,45 @@ async fn test_set_context() { insta::assert_json_snapshot!(response); } + +#[tokio::test(flavor = "multi_thread")] +async fn test_set_context_no_typenames() { + let harness = setup_from_mocks( + json! {{ + "experimental_type_conditioned_fetching": true, + // will make debugging easier + "plugins": { + "experimental.expose_query_plan": true + }, + "include_subgraph_errors": { + "all": true + } + }}, + &[ + ("Subgraph1", include_str!("fixtures/set_context/one.json")), + ("Subgraph2", include_str!("fixtures/set_context/two.json")), + ], + ); + let supergraph_service = harness.build_supergraph().await.unwrap(); + let request = supergraph::Request::fake_builder() + .query(QUERY_NO_TYPENAMES.to_string()) + .header("Apollo-Expose-Query-Plan", "true") + .variables(Default::default()) + .build() + .expect("expecting valid request"); + + let response = supergraph_service + .oneshot(request) + .await + .unwrap() + .next_response() + .await + .unwrap(); + + insta::assert_json_snapshot!(response); +} + + fn setup_from_mocks( configuration: serde_json::Value, mocks: &[(&'static str, &'static str)], @@ -101,3 +140,11 @@ static QUERY: &str = r#"query Query { } } }"#; + static QUERY_NO_TYPENAMES: &str = r#"query Query { + t { + id + u { + field + } + } + }"#; diff --git a/apollo-router/tests/snapshots/set_context__set_context_no_typenames.snap b/apollo-router/tests/snapshots/set_context__set_context_no_typenames.snap new file mode 100644 index 0000000000..fb01498f99 --- /dev/null +++ b/apollo-router/tests/snapshots/set_context__set_context_no_typenames.snap @@ -0,0 +1,112 @@ +--- +source: apollo-router/tests/set_context.rs +expression: response +--- +{ + "data": null, + "extensions": { + "valueCompletion": [ + { + "message": "Cannot return null for non-nullable field U.field", + "path": [ + "t", + "u" + ] + }, + { + "message": "Cannot return null for non-nullable field T.u", + "path": [ + "t", + "u" + ] + }, + { + "message": "Cannot return null for non-nullable field T!.t", + "path": [ + "t" + ] + } + ], + "apolloQueryPlan": { + "object": { + "kind": "QueryPlan", + "node": { + "kind": "Sequence", + "nodes": [ + { + "kind": "Fetch", + "serviceName": "Subgraph1", + "variableUsages": [], + "operation": "query Query__Subgraph1__0{t{prop id u{id}}}", + "operationName": "Query__Subgraph1__0", + "operationKind": "query", + "id": null, + "inputRewrites": null, + "outputRewrites": null, + "contextRewrites": null, + "schemaAwareHash": "0c11d75a9caa25f012a22034f3deff983b512276d0405e155bd83a459d803b72", + "authorization": { + "is_authenticated": false, + "scopes": [], + "policies": [] + } + }, + { + "kind": "Flatten", + "path": [ + "t", + "u" + ], + "node": { + "kind": "Fetch", + "serviceName": "Subgraph1", + "requires": [ + { + "kind": "InlineFragment", + "typeCondition": "U", + "selections": [ + { + "kind": "Field", + "name": "__typename" + }, + { + "kind": "Field", + "name": "id" + } + ] + } + ], + "variableUsages": [ + "Subgraph1_U_field_a" + ], + "operation": "query Query__Subgraph1__1($representations:[_Any!]!$Subgraph1_U_field_a:String!){_entities(representations:$representations){...on U{id field(a:$Subgraph1_U_field_a)}}}", + "operationName": "Query__Subgraph1__1", + "operationKind": "query", + "id": null, + "inputRewrites": null, + "outputRewrites": null, + "contextRewrites": [ + { + "kind": "KeyRenamer", + "path": [ + "..", + "prop" + ], + "renameKeyTo": "Subgraph1_U_field_a" + } + ], + "schemaAwareHash": "82ac499d9905065a835b1ad324b824367a513c6eb3e3b9c0364dd6bd31edb384", + "authorization": { + "is_authenticated": false, + "scopes": [], + "policies": [] + } + } + } + ] + } + }, + "text": "QueryPlan {\n Sequence {\n Fetch(service: \"Subgraph1\") {\n {\n t {\n prop\n id\n u {\n id\n }\n }\n }\n },\n Flatten(path: \"t.u\") {\n Fetch(service: \"Subgraph1\") {\n {\n ... on U {\n __typename\n id\n }\n } =>\n {\n ... on U {\n id\n field(a: $Subgraph1_U_field_a)\n }\n }\n },\n },\n },\n}" + } + } +} From 644cb7a4b2dc083329d1bca7aacc7de9055df570 Mon Sep 17 00:00:00 2001 From: Chris Lenfest Date: Tue, 30 Apr 2024 11:48:17 -0500 Subject: [PATCH 08/69] update tests --- .../tests/fixtures/set_context/one.json | 24 +++++++++++- ...set_context__set_context_no_typenames.snap | 37 ++++++------------- 2 files changed, 34 insertions(+), 27 deletions(-) diff --git a/apollo-router/tests/fixtures/set_context/one.json b/apollo-router/tests/fixtures/set_context/one.json index 62fc98b9eb..09d8d3527e 100644 --- a/apollo-router/tests/fixtures/set_context/one.json +++ b/apollo-router/tests/fixtures/set_context/one.json @@ -21,7 +21,7 @@ }, { "request": { - "query": "query Query__Subgraph1__0{t{prop id u{id}}}", + "query": "query Query__Subgraph1__0{t{prop id u{__typename id}}}", "operationName": "Query__Subgraph1__0" }, "response": { @@ -30,6 +30,7 @@ "prop": "prop value", "id": "1", "u": { + "__typename": "U", "id": "1" } } @@ -56,6 +57,27 @@ ] } } + }, + { + "request": { + "query": "query Query__Subgraph1__1($representations:[_Any!]!$Subgraph1_U_field_a:String!){_entities(representations:$representations){...on U{id field(a:$Subgraph1_U_field_a)}}}", + "operationName": "Query__Subgraph1__1", + "variables": { + "Subgraph1_U_field_a": "prop value", + "representations": [{ "__typename": "U", "id": "1" }] + } + }, + "response": { + "data": { + "_entities": [ + { + "__typename": "U", + "id": "1", + "field": 1234 + } + ] + } + } } ] } diff --git a/apollo-router/tests/snapshots/set_context__set_context_no_typenames.snap b/apollo-router/tests/snapshots/set_context__set_context_no_typenames.snap index fb01498f99..80147400ea 100644 --- a/apollo-router/tests/snapshots/set_context__set_context_no_typenames.snap +++ b/apollo-router/tests/snapshots/set_context__set_context_no_typenames.snap @@ -3,30 +3,15 @@ source: apollo-router/tests/set_context.rs expression: response --- { - "data": null, - "extensions": { - "valueCompletion": [ - { - "message": "Cannot return null for non-nullable field U.field", - "path": [ - "t", - "u" - ] - }, - { - "message": "Cannot return null for non-nullable field T.u", - "path": [ - "t", - "u" - ] - }, - { - "message": "Cannot return null for non-nullable field T!.t", - "path": [ - "t" - ] + "data": { + "t": { + "id": "1", + "u": { + "field": 1234 } - ], + } + }, + "extensions": { "apolloQueryPlan": { "object": { "kind": "QueryPlan", @@ -37,14 +22,14 @@ expression: response "kind": "Fetch", "serviceName": "Subgraph1", "variableUsages": [], - "operation": "query Query__Subgraph1__0{t{prop id u{id}}}", + "operation": "query Query__Subgraph1__0{t{prop id u{__typename id}}}", "operationName": "Query__Subgraph1__0", "operationKind": "query", "id": null, "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "0c11d75a9caa25f012a22034f3deff983b512276d0405e155bd83a459d803b72", + "schemaAwareHash": "cd8e038b3a8b1edee46de7d9f4b75d8acd76315f9ce94353f9d4d36e88fd6da7", "authorization": { "is_authenticated": false, "scopes": [], @@ -106,7 +91,7 @@ expression: response ] } }, - "text": "QueryPlan {\n Sequence {\n Fetch(service: \"Subgraph1\") {\n {\n t {\n prop\n id\n u {\n id\n }\n }\n }\n },\n Flatten(path: \"t.u\") {\n Fetch(service: \"Subgraph1\") {\n {\n ... on U {\n __typename\n id\n }\n } =>\n {\n ... on U {\n id\n field(a: $Subgraph1_U_field_a)\n }\n }\n },\n },\n },\n}" + "text": "QueryPlan {\n Sequence {\n Fetch(service: \"Subgraph1\") {\n {\n t {\n prop\n id\n u {\n __typename\n id\n }\n }\n }\n },\n Flatten(path: \"t.u\") {\n Fetch(service: \"Subgraph1\") {\n {\n ... on U {\n __typename\n id\n }\n } =>\n {\n ... on U {\n id\n field(a: $Subgraph1_U_field_a)\n }\n }\n },\n },\n },\n}" } } } From da6b2b5cb56ce3360cda357bc175c1c03feb03e4 Mon Sep 17 00:00:00 2001 From: Chris Lenfest Date: Tue, 30 Apr 2024 15:44:22 -0500 Subject: [PATCH 09/69] checkpoint. 1 contextual value -> many consumers test --- apollo-router/src/query_planner/fetch.rs | 103 ++++++++++------- apollo-router/src/spec/query.rs | 1 - .../tests/fixtures/set_context/one.json | 59 ++++++++++ .../fixtures/set_context/supergraph.graphql | 2 + apollo-router/tests/set_context.rs | 48 +++++++- .../set_context__set_context_list.snap | 106 ++++++++++++++++++ 6 files changed, 276 insertions(+), 43 deletions(-) create mode 100644 apollo-router/tests/snapshots/set_context__set_context_list.snap diff --git a/apollo-router/src/query_planner/fetch.rs b/apollo-router/src/query_planner/fetch.rs index eb5ced8189..345ab273ae 100644 --- a/apollo-router/src/query_planner/fetch.rs +++ b/apollo-router/src/query_planner/fetch.rs @@ -269,6 +269,49 @@ fn data_at_path<'v>(data: &'v Value, path: &Vec) -> Option<&'v Valu v } +fn merge_context_path(current_dir: &Path, context_path: &Path) -> Vec { + let mut i = 0; + let mut j = current_dir.len(); + // iterate over the context_path(i), every time we encounter a '..', we want + // to go up one level in the current_dir(j) + while i < context_path.len() { + match &context_path.0[i] { + PathElement::Key(e,_) => { + let mut found = false; + if e == ".." { + while !found { + j -= 1; + match current_dir.0[j] { + PathElement::Key(_,_) => { + found = true; + }, + _ => {}, + } + } + i += 1; + } else { + break; + } + } + _ => break, + } + } + + let mut return_path: Vec = current_dir + .iter() + .take(j) + .map(|e| e.clone()) + .collect(); + + context_path + .iter() + .skip(i) + .for_each(|e| { + return_path.push(e.clone()); + }); + return_path +} + impl Variables { #[instrument(skip_all, level = "debug", name = "make_variables")] #[allow(clippy::too_many_arguments)] @@ -287,49 +330,27 @@ impl Variables { if let Some(crw) = context_rewrites { crw.iter().for_each(|rewrite| { if let DataRewrite::KeyRenamer(item) = rewrite { - let up_count = item.path.iter().enumerate().find_map(|(index, p)| match p { - PathElement::Key(key, _) => { - if key != ".." { - Some(index) - } else { - None - } + dbg!(¤t_dir, &item.path); + let data_path = merge_context_path(¤t_dir, &item.path); + + let value = data_at_path(data, &data_path); + let dp: Path = Path(data_path.into_iter().collect()); + let rewrite_with_updated_path = DataRewrite::KeyRenamer({ + DataKeyRenamer { + path: dp, + rename_key_to: item.rename_key_to.clone(), } - _ => Some(index), }); - - // dbg!(¤t_dir); - if let Some(count) = up_count { - let mut data_path: Vec = current_dir - .iter() - .take(current_dir.len() - count) - .map(|e| e.clone()) - .collect(); - item.path - .iter() - .skip(count) - .for_each(|elem| data_path.push(elem.clone())); - let value = data_at_path(data, &data_path); - // dbg!(&data_path); - // dbg!(&data); - let dp: Path = Path(data_path.into_iter().collect()); - let rewrite_with_updated_path = DataRewrite::KeyRenamer({ - DataKeyRenamer { - path: dp, - rename_key_to: item.rename_key_to.clone(), - } - }); - - if let Some(v) = value { - // TODO: not great - let mut new_value = v.clone(); - rewrites::apply_single_rewrite( - schema, - &mut new_value, - &rewrite_with_updated_path, - ); - context_variables.insert(item.rename_key_to.clone(), new_value); - } + dbg!(&rewrite_with_updated_path, &value); + if let Some(v) = value { + // TODO: not great + let mut new_value = v.clone(); + rewrites::apply_single_rewrite( + schema, + &mut new_value, + &rewrite_with_updated_path, + ); + context_variables.insert(item.rename_key_to.clone(), new_value); } } }); diff --git a/apollo-router/src/spec/query.rs b/apollo-router/src/spec/query.rs index aeabaaf449..68fe3a94e4 100644 --- a/apollo-router/src/spec/query.rs +++ b/apollo-router/src/spec/query.rs @@ -793,7 +793,6 @@ impl Query { output: &mut Object, path: &mut Vec>, ) -> Result<(), InvalidValue> { - dbg!(&selection_set, &input, &output, &path); for selection in selection_set { match selection { Selection::Field { diff --git a/apollo-router/tests/fixtures/set_context/one.json b/apollo-router/tests/fixtures/set_context/one.json index 09d8d3527e..3eb56d59f9 100644 --- a/apollo-router/tests/fixtures/set_context/one.json +++ b/apollo-router/tests/fixtures/set_context/one.json @@ -37,6 +37,34 @@ } } }, + { + "request": { + "query": "query Query__Subgraph1__0{t{prop id uList{__typename id}}}", + "operationName": "Query__Subgraph1__0" + }, + "response": { + "data": { + "t": { + "prop": "prop value", + "id": "1", + "uList": [ + { + "__typename": "U", + "id": "1" + }, + { + "__typename": "U", + "id": "2" + }, + { + "__typename": "U", + "id": "3" + } + ] + } + } + } + }, { "request": { "query": "query Query__Subgraph1__1($representations:[_Any!]!$Subgraph1_U_field_a:String!){_entities(representations:$representations){...on U{__typename id field(a:$Subgraph1_U_field_a)}}}", @@ -78,6 +106,37 @@ ] } } + }, + { + "request": { + "query": "query Query__Subgraph1__1($representations:[_Any!]!$Subgraph1_U_field_a:String!){_entities(representations:$representations){...on U{id field(a:$Subgraph1_U_field_a)}}}", + "operationName": "Query__Subgraph1__1", + "variables": { + "Subgraph1_U_field_a": "prop value", + "representations": [{ "__typename": "U", "id": "1" }, { "__typename": "U", "id": "2" }, { "__typename": "U", "id": "3" }] + } + }, + "response": { + "data": { + "_entities": [ + { + "__typename": "U", + "id": "1", + "field": 1234 + }, + { + "__typename": "U", + "id": "2", + "field": 2345 + }, + { + "__typename": "U", + "id": "3", + "field": 3456 + } + ] + } + } } ] } diff --git a/apollo-router/tests/fixtures/set_context/supergraph.graphql b/apollo-router/tests/fixtures/set_context/supergraph.graphql index 77ba17cf44..c5e4066d40 100644 --- a/apollo-router/tests/fixtures/set_context/supergraph.graphql +++ b/apollo-router/tests/fixtures/set_context/supergraph.graphql @@ -92,6 +92,7 @@ enum link__Purpose { type Query @join__type(graph: SUBGRAPH1) @join__type(graph: SUBGRAPH2) { t: T! @join__field(graph: SUBGRAPH1) + tList: [T]! @join__field(graph: SUBGRAPH1) a: Int! @join__field(graph: SUBGRAPH2) } @@ -100,6 +101,7 @@ type T @context(name: "Subgraph1__context") { id: ID! u: U! + uList: [U]! prop: String! } diff --git a/apollo-router/tests/set_context.rs b/apollo-router/tests/set_context.rs index 0a79cdb0b9..545f01a76a 100644 --- a/apollo-router/tests/set_context.rs +++ b/apollo-router/tests/set_context.rs @@ -98,6 +98,43 @@ async fn test_set_context_no_typenames() { insta::assert_json_snapshot!(response); } +#[tokio::test(flavor = "multi_thread")] +async fn test_set_context_list() { + let harness = setup_from_mocks( + json! {{ + "experimental_type_conditioned_fetching": true, + // will make debugging easier + "plugins": { + "experimental.expose_query_plan": true + }, + "include_subgraph_errors": { + "all": true + } + }}, + &[ + ("Subgraph1", include_str!("fixtures/set_context/one.json")), + ("Subgraph2", include_str!("fixtures/set_context/two.json")), + ], + ); + let supergraph_service = harness.build_supergraph().await.unwrap(); + let request = supergraph::Request::fake_builder() + .query(QUERY_WITH_LIST.to_string()) + .header("Apollo-Expose-Query-Plan", "true") + .variables(Default::default()) + .build() + .expect("expecting valid request"); + + let response = supergraph_service + .oneshot(request) + .await + .unwrap() + .next_response() + .await + .unwrap(); + + insta::assert_json_snapshot!(response); +} + fn setup_from_mocks( configuration: serde_json::Value, @@ -140,7 +177,7 @@ static QUERY: &str = r#"query Query { } } }"#; - static QUERY_NO_TYPENAMES: &str = r#"query Query { +static QUERY_NO_TYPENAMES: &str = r#"query Query { t { id u { @@ -148,3 +185,12 @@ static QUERY: &str = r#"query Query { } } }"#; + +static QUERY_WITH_LIST: &str = r#"query Query { + t { + id + uList { + field + } + } + }"#; diff --git a/apollo-router/tests/snapshots/set_context__set_context_list.snap b/apollo-router/tests/snapshots/set_context__set_context_list.snap new file mode 100644 index 0000000000..75dfc8aacc --- /dev/null +++ b/apollo-router/tests/snapshots/set_context__set_context_list.snap @@ -0,0 +1,106 @@ +--- +source: apollo-router/tests/set_context.rs +expression: response +--- +{ + "data": { + "t": { + "id": "1", + "uList": [ + { + "field": 1234 + }, + { + "field": 2345 + }, + { + "field": 3456 + } + ] + } + }, + "extensions": { + "apolloQueryPlan": { + "object": { + "kind": "QueryPlan", + "node": { + "kind": "Sequence", + "nodes": [ + { + "kind": "Fetch", + "serviceName": "Subgraph1", + "variableUsages": [], + "operation": "query Query__Subgraph1__0{t{prop id uList{__typename id}}}", + "operationName": "Query__Subgraph1__0", + "operationKind": "query", + "id": null, + "inputRewrites": null, + "outputRewrites": null, + "contextRewrites": null, + "schemaAwareHash": "9baf30a619593a77ddaf5f68d8bd58af2527c30f0d9f1161001338c0e798edf4", + "authorization": { + "is_authenticated": false, + "scopes": [], + "policies": [] + } + }, + { + "kind": "Flatten", + "path": [ + "t", + "uList", + "@" + ], + "node": { + "kind": "Fetch", + "serviceName": "Subgraph1", + "requires": [ + { + "kind": "InlineFragment", + "typeCondition": "U", + "selections": [ + { + "kind": "Field", + "name": "__typename" + }, + { + "kind": "Field", + "name": "id" + } + ] + } + ], + "variableUsages": [ + "Subgraph1_U_field_a" + ], + "operation": "query Query__Subgraph1__1($representations:[_Any!]!$Subgraph1_U_field_a:String!){_entities(representations:$representations){...on U{id field(a:$Subgraph1_U_field_a)}}}", + "operationName": "Query__Subgraph1__1", + "operationKind": "query", + "id": null, + "inputRewrites": null, + "outputRewrites": null, + "contextRewrites": [ + { + "kind": "KeyRenamer", + "path": [ + "..", + "prop" + ], + "renameKeyTo": "Subgraph1_U_field_a" + } + ], + "schemaAwareHash": "82ac499d9905065a835b1ad324b824367a513c6eb3e3b9c0364dd6bd31edb384", + "authorization": { + "is_authenticated": false, + "scopes": [], + "policies": [] + } + } + } + ] + } + }, + "text": "QueryPlan {\n Sequence {\n Fetch(service: \"Subgraph1\") {\n {\n t {\n prop\n id\n uList {\n __typename\n id\n }\n }\n }\n },\n Flatten(path: \"t.uList.@\") {\n Fetch(service: \"Subgraph1\") {\n {\n ... on U {\n __typename\n id\n }\n } =>\n {\n ... on U {\n id\n field(a: $Subgraph1_U_field_a)\n }\n }\n },\n },\n },\n}" + } + } +} From af096e4bea6a620414b9e276a897ae68928a314b Mon Sep 17 00:00:00 2001 From: Chris Lenfest Date: Wed, 1 May 2024 22:00:32 -0500 Subject: [PATCH 10/69] 5/1 evening checkpoint --- apollo-router/src/query_planner/fetch.rs | 113 ++++++++++-------- .../tests/fixtures/set_context/one.json | 53 ++++++-- .../fixtures/set_context/supergraph.graphql | 2 +- apollo-router/tests/set_context.rs | 45 +++++++ ...ontext__set_context_list_of_lists.snap.new | 93 ++++++++++++++ 5 files changed, 249 insertions(+), 57 deletions(-) create mode 100644 apollo-router/tests/snapshots/set_context__set_context_list_of_lists.snap.new diff --git a/apollo-router/src/query_planner/fetch.rs b/apollo-router/src/query_planner/fetch.rs index 345ab273ae..4868e433c7 100644 --- a/apollo-router/src/query_planner/fetch.rs +++ b/apollo-router/src/query_planner/fetch.rs @@ -251,13 +251,14 @@ pub(crate) struct Variables { pub(crate) inverted_paths: Vec>, } -fn data_at_path<'v>(data: &'v Value, path: &Vec) -> Option<&'v Value> { +// TODO: There is probably a function somewhere else that already does this +fn data_at_path<'v>(data: &'v Value, path: &Path) -> Option<&'v Value> { // TODO: We will have fragments with type conditions that need to be dealt with, but it's not working just yet - let v = match &path[0] { + let v = match &path.0[0] { PathElement::Fragment(s) => data.get(s), - PathElement::Key(v, t) => data.get(v), + PathElement::Key(v, _) => data.get(v), + PathElement::Index(idx) => Some(&data[idx]), PathElement::Flatten(_) => None, - PathElement::Index(_) => None, }; if path.len() > 1 { @@ -269,7 +270,7 @@ fn data_at_path<'v>(data: &'v Value, path: &Vec) -> Option<&'v Valu v } -fn merge_context_path(current_dir: &Path, context_path: &Path) -> Vec { +fn merge_context_path(current_dir: &Path, context_path: &Path) -> Path { let mut i = 0; let mut j = current_dir.len(); // iterate over the context_path(i), every time we encounter a '..', we want @@ -309,7 +310,7 @@ fn merge_context_path(current_dir: &Path, context_path: &Path) -> Vec>, context_rewrites: &Option>, ) -> Option { - let mut context_variables: HashMap = Default::default(); - // apply context_rewrites - if let Some(crw) = context_rewrites { - crw.iter().for_each(|rewrite| { - if let DataRewrite::KeyRenamer(item) = rewrite { - dbg!(¤t_dir, &item.path); - let data_path = merge_context_path(¤t_dir, &item.path); - - let value = data_at_path(data, &data_path); - let dp: Path = Path(data_path.into_iter().collect()); - let rewrite_with_updated_path = DataRewrite::KeyRenamer({ - DataKeyRenamer { - path: dp, - rename_key_to: item.rename_key_to.clone(), - } - }); - dbg!(&rewrite_with_updated_path, &value); - if let Some(v) = value { - // TODO: not great - let mut new_value = v.clone(); - rewrites::apply_single_rewrite( - schema, - &mut new_value, - &rewrite_with_updated_path, - ); - context_variables.insert(item.rename_key_to.clone(), new_value); - } - } - }); - } - - let body = request.body(); let mut variables: serde_json_bytes::Map = Object::with_capacity(1 + variable_usages.len()); - variables.extend(context_variables.iter().map(|(key, value)| { - (key.as_str().into(), value.clone()) - })); - variables.extend(variable_usages.iter().filter_map(|key| { - body.variables - .get_key_value(key.as_str()) - .map(|(variable_key, value)| (variable_key.clone(), value.clone())) - })); if !requires.is_empty() { let mut inverted_paths: Vec> = Vec::new(); let mut values: IndexSet = IndexSet::new(); - + let mut named_args: Vec> = Vec::new(); + dbg!(&data); data.select_values_and_paths(schema, current_dir, |path, value| { + // first get contextual values that are required + let hash_map: HashMap = context_rewrites + .iter() + .flatten() + .filter_map(|rewrite| { + match rewrite { + DataRewrite::KeyRenamer(item) => { + let data_path = merge_context_path(&path, &item.path); + let val = data_at_path(data, &data_path); + dbg!(¤t_dir, &path, &item.path, &data_path); + if let Some(v) = val { + // TODO: not great + let mut new_value = v.clone(); + + rewrites::apply_single_rewrite( + schema, + &mut new_value, + &DataRewrite::KeyRenamer({ + DataKeyRenamer { + path: data_path, + rename_key_to: item.rename_key_to.clone(), + } + }) + ); + return Some((item.rename_key_to.to_string(), new_value)); + } + return None; + }, + DataRewrite::ValueSetter(_) => { + // TODO: Log error? panic? not sure + return None; + }, + } + }) + .collect(); + let mut value = execute_selection_set(value, requires, schema, None); if value.as_object().map(|o| !o.is_empty()).unwrap_or(false) { rewrites::apply_rewrites(schema, &mut value, input_rewrites); match values.get_index_of(&value) { Some(index) => { inverted_paths[index].push(path.clone()); + named_args.push(hash_map); } None => { inverted_paths.push(vec![path.clone()]); values.insert(value); debug_assert!(inverted_paths.len() == values.len()); + named_args.push(hash_map); } } } }); - + if values.is_empty() { return None; } let representations = Value::Array(Vec::from_iter(values)); + dbg!(&named_args); + // TODO: Eventually we need exhibit different behavior depending on whether the named_args are all the same or have differences + if named_args.len() > 0 { + let hash_map = &named_args[0]; + + let body = request.body(); + variables.extend(hash_map.iter().map(|(key, value)| { + (key.as_str().into(), value.clone()) + })); + variables.extend(variable_usages.iter().filter_map(|key| { + body.variables + .get_key_value(key.as_str()) + .map(|(variable_key, value)| (variable_key.clone(), value.clone())) + })); + } variables.insert("representations", representations); Some(Variables { @@ -560,9 +575,9 @@ impl FetchNode { .to_graphql_error(Some(current_dir.to_owned()))], ); } - let (value, errors) = self.response_at_path(parameters.schema, current_dir, paths, response); + dbg!(&value, &errors); if let Some(id) = &self.id { if let Some(sender) = parameters.deferred_fetches.get(id.as_str()) { tracing::info!(monotonic_counter.apollo.router.operations.defer.fetch = 1u64); diff --git a/apollo-router/tests/fixtures/set_context/one.json b/apollo-router/tests/fixtures/set_context/one.json index 3eb56d59f9..47b2a8c98d 100644 --- a/apollo-router/tests/fixtures/set_context/one.json +++ b/apollo-router/tests/fixtures/set_context/one.json @@ -65,6 +65,44 @@ } } }, + { + "request": { + "query": "query QueryLL__Subgraph1__0{tList{prop id uList{__typename id}}}", + "operationName": "QueryLL__Subgraph1__0" + }, + "response": { + "data": [{ + "tList": [{ + "prop": "prop value 1", + "id": "1", + "uList": [ + { + "__typename": "U", + "id": "1" + }, + { + "__typename": "U", + "id": "2" + } + ] + }, + { + "prop": "prop value 2", + "id": "2", + "uList": [ + { + "__typename": "U", + "id": "3" + }, + { + "__typename": "U", + "id": "4" + } + ] + }] + }] + } + }, { "request": { "query": "query Query__Subgraph1__1($representations:[_Any!]!$Subgraph1_U_field_a:String!){_entities(representations:$representations){...on U{__typename id field(a:$Subgraph1_U_field_a)}}}", @@ -109,30 +147,31 @@ }, { "request": { - "query": "query Query__Subgraph1__1($representations:[_Any!]!$Subgraph1_U_field_a:String!){_entities(representations:$representations){...on U{id field(a:$Subgraph1_U_field_a)}}}", - "operationName": "Query__Subgraph1__1", + "query": "query QueryLL__Subgraph1__1($representations:[_Any!]!$Subgraph1_U_field_a:String!){_entities(representations:$representations){...on U{id field(a:$Subgraph1_U_field_a)}}}", + "operationName": "QueryLL__Subgraph1__1", "variables": { - "Subgraph1_U_field_a": "prop value", - "representations": [{ "__typename": "U", "id": "1" }, { "__typename": "U", "id": "2" }, { "__typename": "U", "id": "3" }] + "Subgraph1_U_field_a": "prop value 1", + "representations": [{ "__typename": "U", "id": "1" }, { "__typename": "U", "id": "2" }, { "__typename": "U", "id": "3" }, { "__typename": "U", "id": "4" }] } }, "response": { "data": { "_entities": [ { - "__typename": "U", "id": "1", "field": 1234 }, { - "__typename": "U", "id": "2", "field": 2345 }, { - "__typename": "U", "id": "3", "field": 3456 + }, + { + "id": "4", + "field": 4567 } ] } diff --git a/apollo-router/tests/fixtures/set_context/supergraph.graphql b/apollo-router/tests/fixtures/set_context/supergraph.graphql index c5e4066d40..908e980668 100644 --- a/apollo-router/tests/fixtures/set_context/supergraph.graphql +++ b/apollo-router/tests/fixtures/set_context/supergraph.graphql @@ -109,7 +109,7 @@ type U @join__type(graph: SUBGRAPH1, key: "id") @join__type(graph: SUBGRAPH2, key: "id") { id: ID! - b: String! @join__field(graph: SUBGRAPH1) + b: String! @join__field(graph: SUBGRAPH2) field: Int! @join__field( graph: SUBGRAPH1 diff --git a/apollo-router/tests/set_context.rs b/apollo-router/tests/set_context.rs index 545f01a76a..06b0bce04f 100644 --- a/apollo-router/tests/set_context.rs +++ b/apollo-router/tests/set_context.rs @@ -135,6 +135,42 @@ async fn test_set_context_list() { insta::assert_json_snapshot!(response); } +#[tokio::test(flavor = "multi_thread")] +async fn test_set_context_list_of_lists() { + let harness = setup_from_mocks( + json! {{ + "experimental_type_conditioned_fetching": true, + // will make debugging easier + "plugins": { + "experimental.expose_query_plan": true + }, + "include_subgraph_errors": { + "all": true + } + }}, + &[ + ("Subgraph1", include_str!("fixtures/set_context/one.json")), + ("Subgraph2", include_str!("fixtures/set_context/two.json")), + ], + ); + let supergraph_service = harness.build_supergraph().await.unwrap(); + let request: supergraph::Request = supergraph::Request::fake_builder() + .query(QUERY_WITH_LIST_OF_LISTS.to_string()) + .header("Apollo-Expose-Query-Plan", "true") + .variables(Default::default()) + .build() + .expect("expecting valid request"); + + let response = supergraph_service + .oneshot(request) + .await + .unwrap() + .next_response() + .await + .unwrap(); + + insta::assert_json_snapshot!(response); +} fn setup_from_mocks( configuration: serde_json::Value, @@ -194,3 +230,12 @@ static QUERY_WITH_LIST: &str = r#"query Query { } } }"#; + +static QUERY_WITH_LIST_OF_LISTS: &str = r#"query QueryLL { + tList { + id + uList { + field + } + } + }"#; diff --git a/apollo-router/tests/snapshots/set_context__set_context_list_of_lists.snap.new b/apollo-router/tests/snapshots/set_context__set_context_list_of_lists.snap.new new file mode 100644 index 0000000000..b99e21118f --- /dev/null +++ b/apollo-router/tests/snapshots/set_context__set_context_list_of_lists.snap.new @@ -0,0 +1,93 @@ +--- +source: apollo-router/tests/set_context.rs +assertion_line: 172 +expression: response +--- +{ + "data": null, + "extensions": { + "apolloQueryPlan": { + "object": { + "kind": "QueryPlan", + "node": { + "kind": "Sequence", + "nodes": [ + { + "kind": "Fetch", + "serviceName": "Subgraph1", + "variableUsages": [], + "operation": "query QueryLL__Subgraph1__0{tList{prop id uList{__typename id}}}", + "operationName": "QueryLL__Subgraph1__0", + "operationKind": "query", + "id": null, + "inputRewrites": null, + "outputRewrites": null, + "contextRewrites": null, + "schemaAwareHash": "28135bcdab02ad1954997c68b030f269b89d580a697091e3397dcd058248bae0", + "authorization": { + "is_authenticated": false, + "scopes": [], + "policies": [] + } + }, + { + "kind": "Flatten", + "path": [ + "tList", + "@", + "uList", + "@" + ], + "node": { + "kind": "Fetch", + "serviceName": "Subgraph1", + "requires": [ + { + "kind": "InlineFragment", + "typeCondition": "U", + "selections": [ + { + "kind": "Field", + "name": "__typename" + }, + { + "kind": "Field", + "name": "id" + } + ] + } + ], + "variableUsages": [ + "Subgraph1_U_field_a" + ], + "operation": "query QueryLL__Subgraph1__1($representations:[_Any!]!$Subgraph1_U_field_a:String!){_entities(representations:$representations){...on U{id field(a:$Subgraph1_U_field_a)}}}", + "operationName": "QueryLL__Subgraph1__1", + "operationKind": "query", + "id": null, + "inputRewrites": null, + "outputRewrites": null, + "contextRewrites": [ + { + "kind": "KeyRenamer", + "path": [ + "..", + "prop" + ], + "renameKeyTo": "Subgraph1_U_field_a" + } + ], + "schemaAwareHash": "82ac499d9905065a835b1ad324b824367a513c6eb3e3b9c0364dd6bd31edb384", + "authorization": { + "is_authenticated": false, + "scopes": [], + "policies": [] + } + } + } + ] + } + }, + "text": "QueryPlan {\n Sequence {\n Fetch(service: \"Subgraph1\") {\n {\n tList {\n prop\n id\n uList {\n __typename\n id\n }\n }\n }\n },\n Flatten(path: \"tList.@.uList.@\") {\n Fetch(service: \"Subgraph1\") {\n {\n ... on U {\n __typename\n id\n }\n } =>\n {\n ... on U {\n id\n field(a: $Subgraph1_U_field_a)\n }\n }\n },\n },\n },\n}" + } + } +} From 5d71bf3600869f480cb2258a30543dfe7239b733 Mon Sep 17 00:00:00 2001 From: o0Ignition0o Date: Thu, 2 May 2024 18:15:34 +0200 Subject: [PATCH 11/69] update mock --- Cargo.lock | 2 +- apollo-router/tests/fixtures/set_context/one.json | 6 +----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 85dbdc88b0..30015ed41f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3411,7 +3411,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2 0.5.5", + "socket2 0.4.9", "tokio", "tower-service", "tracing", diff --git a/apollo-router/tests/fixtures/set_context/one.json b/apollo-router/tests/fixtures/set_context/one.json index 47b2a8c98d..6f5f8b3138 100644 --- a/apollo-router/tests/fixtures/set_context/one.json +++ b/apollo-router/tests/fixtures/set_context/one.json @@ -67,7 +67,7 @@ }, { "request": { - "query": "query QueryLL__Subgraph1__0{tList{prop id uList{__typename id}}}", + "query": "query QueryLL__Subgraph1__0{tList{prop id uList{id}}}", "operationName": "QueryLL__Subgraph1__0" }, "response": { @@ -77,11 +77,9 @@ "id": "1", "uList": [ { - "__typename": "U", "id": "1" }, { - "__typename": "U", "id": "2" } ] @@ -91,11 +89,9 @@ "id": "2", "uList": [ { - "__typename": "U", "id": "3" }, { - "__typename": "U", "id": "4" } ] From 5810a9a576fb5e765eb85df115146c7ddef2131a Mon Sep 17 00:00:00 2001 From: o0Ignition0o Date: Thu, 2 May 2024 18:38:38 +0200 Subject: [PATCH 12/69] wip --- apollo-router/src/query_planner/fetch.rs | 36 ++++++++++++++++-------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/apollo-router/src/query_planner/fetch.rs b/apollo-router/src/query_planner/fetch.rs index 4868e433c7..6128433c2a 100644 --- a/apollo-router/src/query_planner/fetch.rs +++ b/apollo-router/src/query_planner/fetch.rs @@ -348,17 +348,31 @@ impl Variables { if let Some(v) = val { // TODO: not great let mut new_value = v.clone(); - - rewrites::apply_single_rewrite( - schema, - &mut new_value, - &DataRewrite::KeyRenamer({ - DataKeyRenamer { - path: data_path, - rename_key_to: item.rename_key_to.clone(), - } - }) - ); + if let Some(values) = new_value.as_array_mut() { + for mut v in values { + rewrites::apply_single_rewrite( + schema, + &mut v, + &DataRewrite::KeyRenamer({ + DataKeyRenamer { + path: data_path.clone(), + rename_key_to: item.rename_key_to.clone(), + } + }) + ); + } + } else { + rewrites::apply_single_rewrite( + schema, + &mut new_value, + &DataRewrite::KeyRenamer({ + DataKeyRenamer { + path: data_path, + rename_key_to: item.rename_key_to.clone(), + } + }) + ); + } return Some((item.rename_key_to.to_string(), new_value)); } return None; From a7efdced56ec71d5c84b23632cc700879ec4e75a Mon Sep 17 00:00:00 2001 From: Chris Lenfest Date: Thu, 2 May 2024 11:48:57 -0500 Subject: [PATCH 13/69] Update to detct condition about when to rename args. --- apollo-router/src/query_planner/fetch.rs | 48 +++++++++++++------ .../tests/fixtures/set_context/one.json | 2 +- 2 files changed, 35 insertions(+), 15 deletions(-) diff --git a/apollo-router/src/query_planner/fetch.rs b/apollo-router/src/query_planner/fetch.rs index 6128433c2a..d3636df55b 100644 --- a/apollo-router/src/query_planner/fetch.rs +++ b/apollo-router/src/query_planner/fetch.rs @@ -409,20 +409,40 @@ impl Variables { let representations = Value::Array(Vec::from_iter(values)); dbg!(&named_args); - // TODO: Eventually we need exhibit different behavior depending on whether the named_args are all the same or have differences - if named_args.len() > 0 { - let hash_map = &named_args[0]; - - let body = request.body(); - variables.extend(hash_map.iter().map(|(key, value)| { - (key.as_str().into(), value.clone()) - })); - variables.extend(variable_usages.iter().filter_map(|key| { - body.variables - .get_key_value(key.as_str()) - .map(|(variable_key, value)| (variable_key.clone(), value.clone())) - })); - } + + // Here we create a new map with all the key value pairs to push into variables. + // Note that if all variables are the same, we just use the named parameter as a variable, but if they are different then each + // entity will have it's own set of parameters all appended by _ + let extended_vars = if let Some(first_map) = named_args.get(0) { + if named_args.iter().all(|map| map == first_map) { + first_map + .iter() + .map(|(k, v)| { + (k.as_str().into(), v.clone()) + }).collect() + } else { + let mut hash_map: HashMap = HashMap::new(); + for (index, item) in named_args.iter().enumerate() { + // append _ to each of the arguments and push all the values into hash_map + hash_map.extend(item.iter().map(|(k, v)| { + let mut new_named_param = k.clone(); + // new_named_param.push_str(&format!("_{}", index)); + (new_named_param, v.clone()) + })); + } + hash_map + } + } else { HashMap::new() }; + + let body = request.body(); + variables.extend(extended_vars.iter().map(|(key, value)| { + (key.as_str().into(), value.clone()) + })); + variables.extend(variable_usages.iter().filter_map(|key| { + body.variables + .get_key_value(key.as_str()) + .map(|(variable_key, value)| (variable_key.clone(), value.clone())) + })); variables.insert("representations", representations); Some(Variables { diff --git a/apollo-router/tests/fixtures/set_context/one.json b/apollo-router/tests/fixtures/set_context/one.json index 6f5f8b3138..9e6a2e5a8e 100644 --- a/apollo-router/tests/fixtures/set_context/one.json +++ b/apollo-router/tests/fixtures/set_context/one.json @@ -146,7 +146,7 @@ "query": "query QueryLL__Subgraph1__1($representations:[_Any!]!$Subgraph1_U_field_a:String!){_entities(representations:$representations){...on U{id field(a:$Subgraph1_U_field_a)}}}", "operationName": "QueryLL__Subgraph1__1", "variables": { - "Subgraph1_U_field_a": "prop value 1", + "Subgraph1_U_field_a": "prop value 2", "representations": [{ "__typename": "U", "id": "1" }, { "__typename": "U", "id": "2" }, { "__typename": "U", "id": "3" }, { "__typename": "U", "id": "4" }] } }, From f2177cd2a3c2d1c723cc47fb8f8dc61d8f757af7 Mon Sep 17 00:00:00 2001 From: Chris Lenfest Date: Thu, 2 May 2024 20:40:20 -0500 Subject: [PATCH 14/69] Adding query batching support for outgoing contextual requests --- apollo-router/src/query_planner/fetch.rs | 86 ++++++++++++++++--- .../tests/fixtures/set_context/one.json | 13 ++- 2 files changed, 85 insertions(+), 14 deletions(-) diff --git a/apollo-router/src/query_planner/fetch.rs b/apollo-router/src/query_planner/fetch.rs index d3636df55b..c8002d1a7e 100644 --- a/apollo-router/src/query_planner/fetch.rs +++ b/apollo-router/src/query_planner/fetch.rs @@ -1,4 +1,5 @@ use std::collections::HashMap; +use std::collections::HashSet; use std::fmt::Display; use std::sync::Arc; use std::sync::OnceLock; @@ -8,6 +9,7 @@ use apollo_compiler::ExecutableDocument; use apollo_compiler::NodeStr; use indexmap::IndexSet; use json_ext::PathElement; +use regex::Regex; use serde::Deserialize; use serde::Serialize; use tower::ServiceExt; @@ -249,6 +251,63 @@ impl Display for QueryHash { pub(crate) struct Variables { pub(crate) variables: Object, pub(crate) inverted_paths: Vec>, + pub(crate) contextual_args: Option<(HashSet, usize)>, +} + +// If contextual_args is set, then all contextual arguments need to be expanded. Non-contextual arguments +// must be preserved +fn expand_contextual_args(args: &str, arg_set: &HashSet, count: usize) -> String { + // it's easiest to just add the $ back in later. + let mut expanded_str = String::new(); + for pair in args.split('$') { + let mut parts = pair.split(':'); + if let (Some(named_param), Some(param_type)) = (parts.next(), parts.next()) { + if arg_set.contains(named_param) { + for i in 0..count { + expanded_str.push_str(&format!("${}_{}:{}", &named_param, i, param_type)); + } + } else { + expanded_str.push_str(&format!("{}:{}", &named_param, param_type)); + } + } + } + expanded_str +} + +fn query_batching_for_contextual_args(old_query: &str, contextual_args: &Option<(HashSet, usize)>) -> Option { + let re = Regex::new(r"(.*?)\((.*?)\).*?(\{.*?\})").unwrap(); + + let result = match contextual_args { + Some((args, count)) => { + match re.captures(old_query) { + Some(caps) => { + match (caps.get(1), caps.get(2), caps.get(3)) { + (Some(query_cruft), Some(params), Some(content)) => { + let params = expand_contextual_args(params.as_str(), args, *count); + let mut new_query: String = query_cruft.as_str().into(); + new_query.push_str(&format!("({})", params.as_str())); + new_query.push_str(" {"); + for i in 0..*count { + let mut cloned_content: String = content.as_str().into(); + for arg in args { + let mut modified_arg = arg.clone(); + modified_arg.push_str(&format!("_{}", i)); + cloned_content = cloned_content.replace(arg, &modified_arg); + } + new_query.push_str(&format!(" _{}:{}", i, &cloned_content)); + } + new_query.push_str("}"); + Some(new_query) + }, + (_, _, _) => None, + } + }, + None => None, + } + }, + None => None, + }; + result } // TODO: There is probably a function somewhere else that already does this @@ -333,7 +392,6 @@ impl Variables { let mut inverted_paths: Vec> = Vec::new(); let mut values: IndexSet = IndexSet::new(); let mut named_args: Vec> = Vec::new(); - dbg!(&data); data.select_values_and_paths(schema, current_dir, |path, value| { // first get contextual values that are required let hash_map: HashMap = context_rewrites @@ -344,7 +402,6 @@ impl Variables { DataRewrite::KeyRenamer(item) => { let data_path = merge_context_path(&path, &item.path); let val = data_at_path(data, &data_path); - dbg!(¤t_dir, &path, &item.path, &data_path); if let Some(v) = val { // TODO: not great let mut new_value = v.clone(); @@ -408,31 +465,31 @@ impl Variables { } let representations = Value::Array(Vec::from_iter(values)); - dbg!(&named_args); // Here we create a new map with all the key value pairs to push into variables. // Note that if all variables are the same, we just use the named parameter as a variable, but if they are different then each // entity will have it's own set of parameters all appended by _ - let extended_vars = if let Some(first_map) = named_args.get(0) { + let (extended_vars, contextual_args) = if let Some(first_map) = named_args.get(0) { if named_args.iter().all(|map| map == first_map) { - first_map + (first_map .iter() .map(|(k, v)| { (k.as_str().into(), v.clone()) - }).collect() + }).collect(), None) } else { let mut hash_map: HashMap = HashMap::new(); + let arg_names: HashSet<_> = first_map.keys().cloned().collect(); for (index, item) in named_args.iter().enumerate() { // append _ to each of the arguments and push all the values into hash_map hash_map.extend(item.iter().map(|(k, v)| { let mut new_named_param = k.clone(); - // new_named_param.push_str(&format!("_{}", index)); + new_named_param.push_str(&format!("_{}", index)); (new_named_param, v.clone()) })); } - hash_map + (hash_map, Some((arg_names, named_args.len()))) } - } else { HashMap::new() }; + } else { (HashMap::new(), None) }; let body = request.body(); variables.extend(extended_vars.iter().map(|(key, value)| { @@ -448,6 +505,7 @@ impl Variables { Some(Variables { variables, inverted_paths, + contextual_args, }) } else { // with nested operations (Query or Mutation has an operation returning a Query or Mutation), @@ -474,6 +532,7 @@ impl Variables { }) .collect::(), inverted_paths: Vec::new(), + contextual_args: None, }) } } @@ -506,6 +565,7 @@ impl FetchNode { let Variables { variables, inverted_paths: paths, + contextual_args, } = match Variables::new( &self.requires, &self.variable_usages, @@ -522,6 +582,11 @@ impl FetchNode { return (Value::Object(Object::default()), Vec::new()); } }; + + let query_batched_query = query_batching_for_contextual_args( + operation.as_serialized(), + &contextual_args + ); let mut subgraph_request = SubgraphRequest::builder() .supergraph_request(parameters.supergraph_request.clone()) @@ -541,7 +606,7 @@ impl FetchNode { ) .body( Request::builder() - .query(operation.as_serialized()) + .query(query_batched_query.as_deref().unwrap_or(operation.as_serialized())) .and_operation_name(operation_name.as_ref().map(|n| n.to_string())) .variables(variables.clone()) .build(), @@ -611,7 +676,6 @@ impl FetchNode { } let (value, errors) = self.response_at_path(parameters.schema, current_dir, paths, response); - dbg!(&value, &errors); if let Some(id) = &self.id { if let Some(sender) = parameters.deferred_fetches.get(id.as_str()) { tracing::info!(monotonic_counter.apollo.router.operations.defer.fetch = 1u64); diff --git a/apollo-router/tests/fixtures/set_context/one.json b/apollo-router/tests/fixtures/set_context/one.json index 9e6a2e5a8e..10d04d0a09 100644 --- a/apollo-router/tests/fixtures/set_context/one.json +++ b/apollo-router/tests/fixtures/set_context/one.json @@ -67,7 +67,7 @@ }, { "request": { - "query": "query QueryLL__Subgraph1__0{tList{prop id uList{id}}}", + "query": "query QueryLL__Subgraph1__0{tList{prop id uList{__typename id}}}", "operationName": "QueryLL__Subgraph1__0" }, "response": { @@ -77,9 +77,11 @@ "id": "1", "uList": [ { + "__typename": "U", "id": "1" }, { + "__typename": "U", "id": "2" } ] @@ -89,9 +91,11 @@ "id": "2", "uList": [ { + "__typename": "U", "id": "3" }, { + "__typename": "U", "id": "4" } ] @@ -143,10 +147,13 @@ }, { "request": { - "query": "query QueryLL__Subgraph1__1($representations:[_Any!]!$Subgraph1_U_field_a:String!){_entities(representations:$representations){...on U{id field(a:$Subgraph1_U_field_a)}}}", + "query": "query QueryLL__Subgraph1__1(representations:[_Any!]!$Subgraph1_U_field_a_0:String!$Subgraph1_U_field_a_1:String!$Subgraph1_U_field_a_2:String!$Subgraph1_U_field_a_3:String!) { _0:{_entities(representations:$representations){...on U{id field(a:$Subgraph1_U_field_a_0)} _1:{_entities(representations:$representations){...on U{id field(a:$Subgraph1_U_field_a_1)} _2:{_entities(representations:$representations){...on U{id field(a:$Subgraph1_U_field_a_2)} _3:{_entities(representations:$representations){...on U{id field(a:$Subgraph1_U_field_a_3)}}", "operationName": "QueryLL__Subgraph1__1", "variables": { - "Subgraph1_U_field_a": "prop value 2", + "Subgraph1_U_field_a_0": "prop value 1", + "Subgraph1_U_field_a_1": "prop value 1", + "Subgraph1_U_field_a_2": "prop value 2", + "Subgraph1_U_field_a_3": "prop value 2", "representations": [{ "__typename": "U", "id": "1" }, { "__typename": "U", "id": "2" }, { "__typename": "U", "id": "3" }, { "__typename": "U", "id": "4" }] } }, From 8412cbdedf6596584a80e737ddbe045c2dac461e Mon Sep 17 00:00:00 2001 From: Chris Lenfest Date: Fri, 3 May 2024 10:03:44 -0500 Subject: [PATCH 15/69] working test --- apollo-router/src/query_planner/fetch.rs | 2 +- .../tests/fixtures/set_context/one.json | 49 +++++++++++-------- ...t_context__set_context_list_of_lists.snap} | 22 ++++++++- 3 files changed, 49 insertions(+), 24 deletions(-) rename apollo-router/tests/snapshots/{set_context__set_context_list_of_lists.snap.new => set_context__set_context_list_of_lists.snap} (92%) diff --git a/apollo-router/src/query_planner/fetch.rs b/apollo-router/src/query_planner/fetch.rs index c8002d1a7e..7a16637e1c 100644 --- a/apollo-router/src/query_planner/fetch.rs +++ b/apollo-router/src/query_planner/fetch.rs @@ -275,7 +275,7 @@ fn expand_contextual_args(args: &str, arg_set: &HashSet, count: usize) - } fn query_batching_for_contextual_args(old_query: &str, contextual_args: &Option<(HashSet, usize)>) -> Option { - let re = Regex::new(r"(.*?)\((.*?)\).*?(\{.*?\})").unwrap(); + let re = Regex::new(r"(.*?)\((.*?)\).*?(\{.*\})").unwrap(); let result = match contextual_args { Some((args, count)) => { diff --git a/apollo-router/tests/fixtures/set_context/one.json b/apollo-router/tests/fixtures/set_context/one.json index 10d04d0a09..1877618803 100644 --- a/apollo-router/tests/fixtures/set_context/one.json +++ b/apollo-router/tests/fixtures/set_context/one.json @@ -71,18 +71,14 @@ "operationName": "QueryLL__Subgraph1__0" }, "response": { - "data": [{ + "data": { "tList": [{ "prop": "prop value 1", "id": "1", "uList": [ { "__typename": "U", - "id": "1" - }, - { - "__typename": "U", - "id": "2" + "id": "3" } ] }, @@ -90,17 +86,13 @@ "prop": "prop value 2", "id": "2", "uList": [ - { - "__typename": "U", - "id": "3" - }, { "__typename": "U", "id": "4" } ] }] - }] + } } }, { @@ -147,27 +139,42 @@ }, { "request": { - "query": "query QueryLL__Subgraph1__1(representations:[_Any!]!$Subgraph1_U_field_a_0:String!$Subgraph1_U_field_a_1:String!$Subgraph1_U_field_a_2:String!$Subgraph1_U_field_a_3:String!) { _0:{_entities(representations:$representations){...on U{id field(a:$Subgraph1_U_field_a_0)} _1:{_entities(representations:$representations){...on U{id field(a:$Subgraph1_U_field_a_1)} _2:{_entities(representations:$representations){...on U{id field(a:$Subgraph1_U_field_a_2)} _3:{_entities(representations:$representations){...on U{id field(a:$Subgraph1_U_field_a_3)}}", + "query": "query QueryLL__Subgraph1__1(representations:[_Any!]!$Subgraph1_U_field_a_0:String!$Subgraph1_U_field_a_1:String!) { _0:{_entities(representations:$representations){...on U{id field(a:$Subgraph1_U_field_a_0)}}} _1:{_entities(representations:$representations){...on U{id field(a:$Subgraph1_U_field_a_1)}}}}", "operationName": "QueryLL__Subgraph1__1", "variables": { "Subgraph1_U_field_a_0": "prop value 1", - "Subgraph1_U_field_a_1": "prop value 1", - "Subgraph1_U_field_a_2": "prop value 2", - "Subgraph1_U_field_a_3": "prop value 2", - "representations": [{ "__typename": "U", "id": "1" }, { "__typename": "U", "id": "2" }, { "__typename": "U", "id": "3" }, { "__typename": "U", "id": "4" }] + "Subgraph1_U_field_a_1": "prop value 2", + "representations": [{ "__typename": "U", "id": "3" }, { "__typename": "U", "id": "4" }] } }, "response": { "data": { "_entities": [ { - "id": "1", - "field": 1234 + "id": "3", + "field": 3456 }, { - "id": "2", - "field": 2345 - }, + "id": "4", + "field": 4567 + } + ] + } + } + }, + { + "request": { + "query": "query QueryLL__Subgraph1__1(representations:[_Any!]!$Subgraph1_U_field_a_0:String!$Subgraph1_U_field_a_1:String!) { _0:{_entities(representations:$representations){...on U{id field(a:$Subgraph1_U_field_a_0)}}} _1:{_entities(representations:$representations){...on U{id field(a:$Subgraph1_U_field_a_1)}}}}", + "operationName": "QueryLL__Subgraph1__1", + "variables": { + "Subgraph1_U_field_a_1": "prop value 2", + "Subgraph1_U_field_a_0": "prop value 1", + "representations": [{ "__typename": "U", "id": "3" }, { "__typename": "U", "id": "4" }] + } + }, + "response": { + "data": { + "_entities": [ { "id": "3", "field": 3456 diff --git a/apollo-router/tests/snapshots/set_context__set_context_list_of_lists.snap.new b/apollo-router/tests/snapshots/set_context__set_context_list_of_lists.snap similarity index 92% rename from apollo-router/tests/snapshots/set_context__set_context_list_of_lists.snap.new rename to apollo-router/tests/snapshots/set_context__set_context_list_of_lists.snap index b99e21118f..3d5e713c1a 100644 --- a/apollo-router/tests/snapshots/set_context__set_context_list_of_lists.snap.new +++ b/apollo-router/tests/snapshots/set_context__set_context_list_of_lists.snap @@ -1,10 +1,28 @@ --- source: apollo-router/tests/set_context.rs -assertion_line: 172 expression: response --- { - "data": null, + "data": { + "tList": [ + { + "id": "1", + "uList": [ + { + "field": 3456 + } + ] + }, + { + "id": "2", + "uList": [ + { + "field": 4567 + } + ] + } + ] + }, "extensions": { "apolloQueryPlan": { "object": { From 54d5c78d0a3c416dae48f1883f9afac3d6571d48 Mon Sep 17 00:00:00 2001 From: Chris Lenfest Date: Fri, 3 May 2024 10:11:42 -0500 Subject: [PATCH 16/69] updating pinned releases --- apollo-router/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apollo-router/Cargo.toml b/apollo-router/Cargo.toml index f284220fc9..e3eab0a14b 100644 --- a/apollo-router/Cargo.toml +++ b/apollo-router/Cargo.toml @@ -65,7 +65,7 @@ askama = "0.12.1" access-json = "0.1.0" anyhow = "1.0.80" apollo-compiler.workspace = true -apollo-federation = { path = "../../federation-next" } +apollo-federation = { git = "https://github.com/apollo/federation-next", rev = "7d25ce0d4721e3c00b2300b9377777826e159474" } arc-swap = "1.6.0" async-channel = "1.9.0" async-compression = { version = "0.4.6", features = [ @@ -183,7 +183,7 @@ regex = "1.10.3" reqwest.workspace = true # note: this dependency should _always_ be pinned, prefix the version with an `=` -router-bridge = { path = "../../federation-rs/federation-2/router-bridge" } +router-bridge = { git = "https://github.com/apollo/federation-rs", rev = "499dc5d51839011093b66d89aeb8bc8ac0d9f908" } rust-embed = "8.2.0" rustls = "0.21.11" From db394b53fe9bfd0dcb429b7b1ba10a46ea90e72d Mon Sep 17 00:00:00 2001 From: Chris Lenfest Date: Fri, 3 May 2024 10:16:16 -0500 Subject: [PATCH 17/69] updates to git paths --- Cargo.lock | 29 +++++++++++++++++++++++++++-- apollo-router/Cargo.toml | 4 ++-- 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 30015ed41f..93bd1a1baf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -221,6 +221,7 @@ dependencies = [ [[package]] name = "apollo-federation" version = "0.0.11" +source = "git+https://github.com/apollographql/federation-next?rev=7d25ce0d4721e3c00b2300b9377777826e159474#7d25ce0d4721e3c00b2300b9377777826e159474" dependencies = [ "apollo-compiler", "derive_more", @@ -349,7 +350,7 @@ dependencies = [ "regex", "reqwest", "rhai", - "router-bridge", + "router-bridge 0.5.20+v2.7.4 (git+https://github.com/apollographql/federation-rs?rev=499dc5d51839011093b66d89aeb8bc8ac0d9f908)", "rstack", "rust-embed", "rustls", @@ -5723,6 +5724,30 @@ dependencies = [ "which", ] +[[package]] +name = "router-bridge" +version = "0.5.20+v2.7.4" +source = "git+https://github.com/apollographql/federation-rs?rev=499dc5d51839011093b66d89aeb8bc8ac0d9f908#499dc5d51839011093b66d89aeb8bc8ac0d9f908" +dependencies = [ + "anyhow", + "async-channel 1.9.0", + "deno_console", + "deno_core", + "deno_crypto", + "deno_url", + "deno_web", + "deno_webidl", + "rand 0.8.5", + "serde", + "serde_json", + "thiserror", + "tokio", + "tower", + "tower-service", + "tracing", + "which", +] + [[package]] name = "router-fuzz" version = "0.0.0" @@ -5738,7 +5763,7 @@ dependencies = [ "libfuzzer-sys", "log", "reqwest", - "router-bridge", + "router-bridge 0.5.20+v2.7.4", "schemars", "serde", "serde_json", diff --git a/apollo-router/Cargo.toml b/apollo-router/Cargo.toml index e3eab0a14b..08ce0dd6b3 100644 --- a/apollo-router/Cargo.toml +++ b/apollo-router/Cargo.toml @@ -65,7 +65,7 @@ askama = "0.12.1" access-json = "0.1.0" anyhow = "1.0.80" apollo-compiler.workspace = true -apollo-federation = { git = "https://github.com/apollo/federation-next", rev = "7d25ce0d4721e3c00b2300b9377777826e159474" } +apollo-federation = { git = "https://github.com/apollographql/federation-next", rev = "7d25ce0d4721e3c00b2300b9377777826e159474" } arc-swap = "1.6.0" async-channel = "1.9.0" async-compression = { version = "0.4.6", features = [ @@ -183,7 +183,7 @@ regex = "1.10.3" reqwest.workspace = true # note: this dependency should _always_ be pinned, prefix the version with an `=` -router-bridge = { git = "https://github.com/apollo/federation-rs", rev = "499dc5d51839011093b66d89aeb8bc8ac0d9f908" } +router-bridge = { git = "https://github.com/apollographql/federation-rs", rev = "499dc5d51839011093b66d89aeb8bc8ac0d9f908" } rust-embed = "8.2.0" rustls = "0.21.11" From 4b8572bd04e6e7864b19c6c910c2ad7fd364c147 Mon Sep 17 00:00:00 2001 From: o0Ignition0o Date: Sat, 4 May 2024 19:03:13 +0200 Subject: [PATCH 18/69] use apollo parser primitives to spread variables in the subgraph operation --- apollo-router/src/query_planner/fetch.rs | 256 +++++++++++------- .../tests/fixtures/set_context/one.json | 92 +++++-- apollo-router/tests/set_context.rs | 61 ++--- 3 files changed, 252 insertions(+), 157 deletions(-) diff --git a/apollo-router/src/query_planner/fetch.rs b/apollo-router/src/query_planner/fetch.rs index 7a16637e1c..386aa8c5e9 100644 --- a/apollo-router/src/query_planner/fetch.rs +++ b/apollo-router/src/query_planner/fetch.rs @@ -4,12 +4,14 @@ use std::fmt::Display; use std::sync::Arc; use std::sync::OnceLock; +use apollo_compiler::ast; +use apollo_compiler::ast::Name; use apollo_compiler::validation::Valid; use apollo_compiler::ExecutableDocument; +use apollo_compiler::Node; use apollo_compiler::NodeStr; use indexmap::IndexSet; use json_ext::PathElement; -use regex::Regex; use serde::Deserialize; use serde::Serialize; use tower::ServiceExt; @@ -74,22 +76,22 @@ impl OperationKind { } } -impl From for apollo_compiler::ast::OperationType { +impl From for ast::OperationType { fn from(value: OperationKind) -> Self { match value { - OperationKind::Query => apollo_compiler::ast::OperationType::Query, - OperationKind::Mutation => apollo_compiler::ast::OperationType::Mutation, - OperationKind::Subscription => apollo_compiler::ast::OperationType::Subscription, + OperationKind::Query => ast::OperationType::Query, + OperationKind::Mutation => ast::OperationType::Mutation, + OperationKind::Subscription => ast::OperationType::Subscription, } } } -impl From for OperationKind { - fn from(value: apollo_compiler::ast::OperationType) -> Self { +impl From for OperationKind { + fn from(value: ast::OperationType) -> Self { match value { - apollo_compiler::ast::OperationType::Query => OperationKind::Query, - apollo_compiler::ast::OperationType::Mutation => OperationKind::Mutation, - apollo_compiler::ast::OperationType::Subscription => OperationKind::Subscription, + ast::OperationType::Query => OperationKind::Query, + ast::OperationType::Mutation => OperationKind::Mutation, + ast::OperationType::Subscription => OperationKind::Subscription, } } } @@ -254,60 +256,117 @@ pub(crate) struct Variables { pub(crate) contextual_args: Option<(HashSet, usize)>, } -// If contextual_args is set, then all contextual arguments need to be expanded. Non-contextual arguments -// must be preserved -fn expand_contextual_args(args: &str, arg_set: &HashSet, count: usize) -> String { - // it's easiest to just add the $ back in later. - let mut expanded_str = String::new(); - for pair in args.split('$') { - let mut parts = pair.split(':'); - if let (Some(named_param), Some(param_type)) = (parts.next(), parts.next()) { - if arg_set.contains(named_param) { - for i in 0..count { - expanded_str.push_str(&format!("${}_{}:{}", &named_param, i, param_type)); - } - } else { - expanded_str.push_str(&format!("{}:{}", &named_param, param_type)); +fn query_batching_for_contextual_args2( + operation: &str, + contextual_args: &Option<(HashSet, usize)>, +) -> Option { + if let Some((ctx, times)) = contextual_args { + let parser = apollo_compiler::Parser::new() + .parse_ast(operation, "") + // TODO: remove unwrap + .unwrap(); + if let Some(mut operation) = parser + .definitions + .into_iter() + .find_map(|definition| definition.as_operation_definition().cloned()) + { + let mut new_variables: Vec<_> = Default::default(); + if operation + .variables + .iter() + .any(|v| ctx.contains(v.name.as_str())) + { + let new_selection_set: Vec<_> = (0..*times) + .into_iter() + .map(|i| { + // TODO: Unwrap + let mut s = operation.selection_set.first().unwrap().clone(); + if let ast::Selection::Field(f) = &mut s { + let f = f.make_mut(); + f.alias = Some(Name::new(format!("_{}", i)).unwrap()); + } + + for v in &operation.variables { + if ctx.contains(v.name.as_str()) { + let mut cloned = v.clone(); + let new_variable = cloned.make_mut(); + // TODO: remove unwrap + new_variable.name = Name::new(format!("{}_{}", v.name, i)).unwrap(); + new_variables.push(Node::new(new_variable.clone())); + + s = rename_variables(s, v.name.clone(), new_variable.name.clone()); + } else { + if !new_variables.iter().any(|var| var.name == v.name) { + new_variables.push(v.clone()); + } + } + } + + s + }) + .collect(); + + let new_operation = operation.make_mut(); + new_operation.selection_set = new_selection_set; + new_operation.variables = new_variables; + + return Some(new_operation.serialize().no_indent().to_string()); } } } - expanded_str + + None } -fn query_batching_for_contextual_args(old_query: &str, contextual_args: &Option<(HashSet, usize)>) -> Option { - let re = Regex::new(r"(.*?)\((.*?)\).*?(\{.*\})").unwrap(); - - let result = match contextual_args { - Some((args, count)) => { - match re.captures(old_query) { - Some(caps) => { - match (caps.get(1), caps.get(2), caps.get(3)) { - (Some(query_cruft), Some(params), Some(content)) => { - let params = expand_contextual_args(params.as_str(), args, *count); - let mut new_query: String = query_cruft.as_str().into(); - new_query.push_str(&format!("({})", params.as_str())); - new_query.push_str(" {"); - for i in 0..*count { - let mut cloned_content: String = content.as_str().into(); - for arg in args { - let mut modified_arg = arg.clone(); - modified_arg.push_str(&format!("_{}", i)); - cloned_content = cloned_content.replace(arg, &modified_arg); - } - new_query.push_str(&format!(" _{}:{}", i, &cloned_content)); - } - new_query.push_str("}"); - Some(new_query) - }, - (_, _, _) => None, - } - }, - None => None, - } - }, - None => None, - }; - result +fn rename_variables(selection_set: ast::Selection, from: Name, to: Name) -> ast::Selection { + match selection_set { + ast::Selection::Field(f) => { + let mut new = f.clone(); + + let as_mut = new.make_mut(); + + as_mut.arguments.iter_mut().for_each(|arg| { + if arg.value.as_variable() == Some(&from) { + arg.make_mut().value = ast::Value::Variable(to.clone()).into(); + } + }); + + as_mut.selection_set = as_mut + .selection_set + .clone() + .into_iter() + .map(|s| rename_variables(s, from.clone(), to.clone())) + .collect(); + + ast::Selection::Field(new) + } + ast::Selection::InlineFragment(f) => { + let mut new = f.clone(); + new.make_mut().selection_set = f + .selection_set + .clone() + .into_iter() + .map(|s| rename_variables(s, from.clone(), to.clone())) + .collect(); + ast::Selection::InlineFragment(new) + } + ast::Selection::FragmentSpread(f) => ast::Selection::FragmentSpread(f), + } +} + +#[test] +fn test_query_batching_for_contextual_args() { + let old_query = "query QueryLL__Subgraph1__1($representations:[_Any!]!$Subgraph1_U_field_a:String!){_entities(representations:$representations){...on U{id field(a:$Subgraph1_U_field_a)}}}"; + let mut ctx_args = HashSet::new(); + ctx_args.insert("Subgraph1_U_field_a".to_string()); + let contextual_args = Some((ctx_args, 2)); + + let expected = "query QueryLL__Subgraph1__1($representations: [_Any!]!, $Subgraph1_U_field_a_0: String!, $Subgraph1_U_field_a_1: String!) { _0: _entities(representations: $representations) { ... on U { id field(a: $Subgraph1_U_field_a_0) } } _1: _entities(representations: $representations) { ... on U { id field(a: $Subgraph1_U_field_a_1) } } }"; + + assert_eq!( + expected, + query_batching_for_contextual_args2(old_query, &contextual_args).unwrap() + ); } // TODO: There is probably a function somewhere else that already does this @@ -332,20 +391,20 @@ fn data_at_path<'v>(data: &'v Value, path: &Path) -> Option<&'v Value> { fn merge_context_path(current_dir: &Path, context_path: &Path) -> Path { let mut i = 0; let mut j = current_dir.len(); - // iterate over the context_path(i), every time we encounter a '..', we want + // iterate over the context_path(i), every time we encounter a '..', we want // to go up one level in the current_dir(j) while i < context_path.len() { match &context_path.0[i] { - PathElement::Key(e,_) => { + PathElement::Key(e, _) => { let mut found = false; if e == ".." { while !found { j -= 1; match current_dir.0[j] { - PathElement::Key(_,_) => { + PathElement::Key(_, _) => { found = true; - }, - _ => {}, + } + _ => {} } } i += 1; @@ -356,19 +415,12 @@ fn merge_context_path(current_dir: &Path, context_path: &Path) -> Path { _ => break, } } - - let mut return_path: Vec = current_dir - .iter() - .take(j) - .map(|e| e.clone()) - .collect(); - - context_path - .iter() - .skip(i) - .for_each(|e| { - return_path.push(e.clone()); - }); + + let mut return_path: Vec = current_dir.iter().take(j).map(|e| e.clone()).collect(); + + context_path.iter().skip(i).for_each(|e| { + return_path.push(e.clone()); + }); Path(return_path.into_iter().collect()) } @@ -415,7 +467,7 @@ impl Variables { path: data_path.clone(), rename_key_to: item.rename_key_to.clone(), } - }) + }), ); } } else { @@ -427,21 +479,21 @@ impl Variables { path: data_path, rename_key_to: item.rename_key_to.clone(), } - }) + }), ); } return Some((item.rename_key_to.to_string(), new_value)); } return None; - }, + } DataRewrite::ValueSetter(_) => { // TODO: Log error? panic? not sure return None; - }, + } } }) .collect(); - + let mut value = execute_selection_set(value, requires, schema, None); if value.as_object().map(|o| !o.is_empty()).unwrap_or(false) { rewrites::apply_rewrites(schema, &mut value, input_rewrites); @@ -459,23 +511,25 @@ impl Variables { } } }); - + if values.is_empty() { return None; } let representations = Value::Array(Vec::from_iter(values)); - + // Here we create a new map with all the key value pairs to push into variables. // Note that if all variables are the same, we just use the named parameter as a variable, but if they are different then each // entity will have it's own set of parameters all appended by _ let (extended_vars, contextual_args) = if let Some(first_map) = named_args.get(0) { if named_args.iter().all(|map| map == first_map) { - (first_map - .iter() - .map(|(k, v)| { - (k.as_str().into(), v.clone()) - }).collect(), None) + ( + first_map + .iter() + .map(|(k, v)| (k.as_str().into(), v.clone())) + .collect(), + None, + ) } else { let mut hash_map: HashMap = HashMap::new(); let arg_names: HashSet<_> = first_map.keys().cloned().collect(); @@ -489,17 +543,21 @@ impl Variables { } (hash_map, Some((arg_names, named_args.len()))) } - } else { (HashMap::new(), None) }; - + } else { + (HashMap::new(), None) + }; + let body = request.body(); - variables.extend(extended_vars.iter().map(|(key, value)| { - (key.as_str().into(), value.clone()) - })); + variables.extend( + extended_vars + .iter() + .map(|(key, value)| (key.as_str().into(), value.clone())), + ); variables.extend(variable_usages.iter().filter_map(|key| { body.variables .get_key_value(key.as_str()) .map(|(variable_key, value)| (variable_key.clone(), value.clone())) - })); + })); variables.insert("representations", representations); Some(Variables { @@ -582,11 +640,9 @@ impl FetchNode { return (Value::Object(Object::default()), Vec::new()); } }; - - let query_batched_query = query_batching_for_contextual_args( - operation.as_serialized(), - &contextual_args - ); + + let query_batched_query = + query_batching_for_contextual_args2(operation.as_serialized(), &contextual_args); let mut subgraph_request = SubgraphRequest::builder() .supergraph_request(parameters.supergraph_request.clone()) diff --git a/apollo-router/tests/fixtures/set_context/one.json b/apollo-router/tests/fixtures/set_context/one.json index 1877618803..70d5d3fc23 100644 --- a/apollo-router/tests/fixtures/set_context/one.json +++ b/apollo-router/tests/fixtures/set_context/one.json @@ -72,29 +72,31 @@ }, "response": { "data": { - "tList": [{ - "prop": "prop value 1", - "id": "1", - "uList": [ - { - "__typename": "U", - "id": "3" - } - ] - }, - { - "prop": "prop value 2", - "id": "2", - "uList": [ - { - "__typename": "U", - "id": "4" - } - ] - }] + "tList": [ + { + "prop": "prop value 1", + "id": "1", + "uList": [ + { + "__typename": "U", + "id": "3" + } + ] + }, + { + "prop": "prop value 2", + "id": "2", + "uList": [ + { + "__typename": "U", + "id": "4" + } + ] + } + ] + } } - } - }, + }, { "request": { "query": "query Query__Subgraph1__1($representations:[_Any!]!$Subgraph1_U_field_a:String!){_entities(representations:$representations){...on U{__typename id field(a:$Subgraph1_U_field_a)}}}", @@ -139,12 +141,47 @@ }, { "request": { - "query": "query QueryLL__Subgraph1__1(representations:[_Any!]!$Subgraph1_U_field_a_0:String!$Subgraph1_U_field_a_1:String!) { _0:{_entities(representations:$representations){...on U{id field(a:$Subgraph1_U_field_a_0)}}} _1:{_entities(representations:$representations){...on U{id field(a:$Subgraph1_U_field_a_1)}}}}", + "query": "query Query__Subgraph1__1($representations:[_Any!]!$Subgraph1_U_field_a:String!){_entities(representations:$representations){...on U{id field(a:$Subgraph1_U_field_a)}}}", + "operationName": "Query__Subgraph1__1", + "variables": { + "Subgraph1_U_field_a": "prop value", + "representations": [ + { "__typename": "U", "id": "1" }, + { "__typename": "U", "id": "2" }, + { "__typename": "U", "id": "3" } + ] + } + }, + "response": { + "data": { + "_entities": [ + { + "id": "1", + "field": 1234 + }, + { + "id": "2", + "field": 2345 + }, + { + "id": "3", + "field": 3456 + } + ] + } + } + }, + { + "request": { + "query": "query QueryLL__Subgraph1__1($representations: [_Any!]!, $Subgraph1_U_field_a_0: String!, $Subgraph1_U_field_a_1: String!) { _0: _entities(representations: $representations) { ... on U { id field(a: $Subgraph1_U_field_a_0) } } _1: _entities(representations: $representations) { ... on U { id field(a: $Subgraph1_U_field_a_1) } } }", "operationName": "QueryLL__Subgraph1__1", "variables": { "Subgraph1_U_field_a_0": "prop value 1", "Subgraph1_U_field_a_1": "prop value 2", - "representations": [{ "__typename": "U", "id": "3" }, { "__typename": "U", "id": "4" }] + "representations": [ + { "__typename": "U", "id": "3" }, + { "__typename": "U", "id": "4" } + ] } }, "response": { @@ -164,12 +201,15 @@ }, { "request": { - "query": "query QueryLL__Subgraph1__1(representations:[_Any!]!$Subgraph1_U_field_a_0:String!$Subgraph1_U_field_a_1:String!) { _0:{_entities(representations:$representations){...on U{id field(a:$Subgraph1_U_field_a_0)}}} _1:{_entities(representations:$representations){...on U{id field(a:$Subgraph1_U_field_a_1)}}}}", + "query": "query QueryLL__Subgraph1__1($representations: [_Any!]!, $Subgraph1_U_field_a_0: String!, $Subgraph1_U_field_a_1: String!) { _0: _entities(representations: $representations) { ... on U { id field(a: $Subgraph1_U_field_a_0) } } _1: _entities(representations: $representations) { ... on U { id field(a: $Subgraph1_U_field_a_1) } } }", "operationName": "QueryLL__Subgraph1__1", "variables": { "Subgraph1_U_field_a_1": "prop value 2", "Subgraph1_U_field_a_0": "prop value 1", - "representations": [{ "__typename": "U", "id": "3" }, { "__typename": "U", "id": "4" }] + "representations": [ + { "__typename": "U", "id": "3" }, + { "__typename": "U", "id": "4" } + ] } }, "response": { diff --git a/apollo-router/tests/set_context.rs b/apollo-router/tests/set_context.rs index 06b0bce04f..d471051955 100644 --- a/apollo-router/tests/set_context.rs +++ b/apollo-router/tests/set_context.rs @@ -60,7 +60,6 @@ async fn test_set_context() { insta::assert_json_snapshot!(response); } - #[tokio::test(flavor = "multi_thread")] async fn test_set_context_no_typenames() { let harness = setup_from_mocks( @@ -204,38 +203,38 @@ fn setup_from_mocks( // TODO[clenfest]: figure out why i need __typename here? static QUERY: &str = r#"query Query { - t { - __typename - id - u { - __typename - field - } - } - }"#; + t { + __typename + id + u { + __typename + field + } + } + }"#; static QUERY_NO_TYPENAMES: &str = r#"query Query { - t { - id - u { - field - } - } - }"#; + t { + id + u { + field + } + } + }"#; static QUERY_WITH_LIST: &str = r#"query Query { - t { - id - uList { - field - } - } - }"#; + t { + id + uList { + field + } + } + }"#; static QUERY_WITH_LIST_OF_LISTS: &str = r#"query QueryLL { - tList { - id - uList { - field - } - } - }"#; + tList { + id + uList { + field + } + } + }"#; From 3db9aa164a6f268a82c8543c60f4fc1bb7e86c8f Mon Sep 17 00:00:00 2001 From: Chris Lenfest Date: Mon, 6 May 2024 20:14:14 -0500 Subject: [PATCH 19/69] We allow context_rewrites to contain multiple rewrites for a single variable. Use the first one we find --- apollo-router/src/query_planner/fetch.rs | 49 ++++++++++++++---------- 1 file changed, 28 insertions(+), 21 deletions(-) diff --git a/apollo-router/src/query_planner/fetch.rs b/apollo-router/src/query_planner/fetch.rs index 386aa8c5e9..b2d7a2696d 100644 --- a/apollo-router/src/query_planner/fetch.rs +++ b/apollo-router/src/query_planner/fetch.rs @@ -446,43 +446,50 @@ impl Variables { let mut named_args: Vec> = Vec::new(); data.select_values_and_paths(schema, current_dir, |path, value| { // first get contextual values that are required + let mut found_rewrites: HashSet = HashSet::new(); let hash_map: HashMap = context_rewrites .iter() .flatten() .filter_map(|rewrite| { + // note that it's possible that we could have multiple rewrites for the same variable. If that's the case, don't lookup if a value has already been found match rewrite { DataRewrite::KeyRenamer(item) => { - let data_path = merge_context_path(&path, &item.path); - let val = data_at_path(data, &data_path); - if let Some(v) = val { - // TODO: not great - let mut new_value = v.clone(); - if let Some(values) = new_value.as_array_mut() { - for mut v in values { + if !found_rewrites.contains(item.rename_key_to.as_str()) { + let data_path = merge_context_path(&path, &item.path); + let val = data_at_path(data, &data_path); + if let Some(v) = val { + // add to found + found_rewrites.insert(item.rename_key_to.clone().to_string()); + + // TODO: not great + let mut new_value = v.clone(); + if let Some(values) = new_value.as_array_mut() { + for mut v in values { + rewrites::apply_single_rewrite( + schema, + &mut v, + &DataRewrite::KeyRenamer({ + DataKeyRenamer { + path: data_path.clone(), + rename_key_to: item.rename_key_to.clone(), + } + }), + ); + } + } else { rewrites::apply_single_rewrite( schema, - &mut v, + &mut new_value, &DataRewrite::KeyRenamer({ DataKeyRenamer { - path: data_path.clone(), + path: data_path, rename_key_to: item.rename_key_to.clone(), } }), ); } - } else { - rewrites::apply_single_rewrite( - schema, - &mut new_value, - &DataRewrite::KeyRenamer({ - DataKeyRenamer { - path: data_path, - rename_key_to: item.rename_key_to.clone(), - } - }), - ); + return Some((item.rename_key_to.to_string(), new_value)); } - return Some((item.rename_key_to.to_string(), new_value)); } return None; } From 0cf5d3d3add5241c92b55537f9e0fe74b73bfc2d Mon Sep 17 00:00:00 2001 From: Chris Lenfest Date: Mon, 6 May 2024 22:17:21 -0500 Subject: [PATCH 20/69] make changes to apollo-federation to use context_rewrites --- Cargo.lock | 28 +++++++++++++++++-- apollo-federation/src/query_plan/display.rs | 1 + .../src/query_plan/fetch_dependency_graph.rs | 1 + apollo-federation/src/query_plan/mod.rs | 3 ++ .../src/query_plan/query_planner.rs | 1 + apollo-router/Cargo.toml | 2 +- apollo-router/src/query_planner/tests.rs | 1 + 7 files changed, 34 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6147519513..55d298ea7e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -360,7 +360,7 @@ dependencies = [ "regex", "reqwest", "rhai", - "router-bridge 0.5.20+v2.7.4 (git+https://github.com/apollographql/federation-rs?rev=499dc5d51839011093b66d89aeb8bc8ac0d9f908)", + "router-bridge 0.5.20+v2.7.4", "rstack", "rust-embed", "rustls", @@ -5719,6 +5719,30 @@ dependencies = [ "paste", ] +[[package]] +name = "router-bridge" +version = "0.5.20+v2.7.4" +source = "git+https://github.com/apollographql/federation-rs?rev=499dc5d51839011093b66d89aeb8bc8ac0d9f908#499dc5d51839011093b66d89aeb8bc8ac0d9f908" +dependencies = [ + "anyhow", + "async-channel 1.9.0", + "deno_console", + "deno_core", + "deno_crypto", + "deno_url", + "deno_web", + "deno_webidl", + "rand 0.8.5", + "serde", + "serde_json", + "thiserror", + "tokio", + "tower", + "tower-service", + "tracing", + "which", +] + [[package]] name = "router-bridge" version = "0.5.21+v2.7.5" @@ -5759,7 +5783,7 @@ dependencies = [ "libfuzzer-sys", "log", "reqwest", - "router-bridge 0.5.20+v2.7.4", + "router-bridge 0.5.21+v2.7.5", "schemars", "serde", "serde_json", diff --git a/apollo-federation/src/query_plan/display.rs b/apollo-federation/src/query_plan/display.rs index 59ef397c38..5a217a3a87 100644 --- a/apollo-federation/src/query_plan/display.rs +++ b/apollo-federation/src/query_plan/display.rs @@ -83,6 +83,7 @@ impl FetchNode { operation_kind: _, input_rewrites: _, output_rewrites: _, + context_rewrites: _, } = self; state.write(format_args!("Fetch(service: {subgraph_name:?}"))?; if let Some(id) = id { diff --git a/apollo-federation/src/query_plan/fetch_dependency_graph.rs b/apollo-federation/src/query_plan/fetch_dependency_graph.rs index 22016e4659..0c0c605f27 100644 --- a/apollo-federation/src/query_plan/fetch_dependency_graph.rs +++ b/apollo-federation/src/query_plan/fetch_dependency_graph.rs @@ -1304,6 +1304,7 @@ impl FetchDependencyGraphNode { operation_kind: self.root_kind.into(), input_rewrites: self.input_rewrites.clone(), output_rewrites, + context_rewrites: Default::default(), })); Ok(Some(if let Some(path) = self.merge_at.clone() { diff --git a/apollo-federation/src/query_plan/mod.rs b/apollo-federation/src/query_plan/mod.rs index 7a27999455..eb7ffe5085 100644 --- a/apollo-federation/src/query_plan/mod.rs +++ b/apollo-federation/src/query_plan/mod.rs @@ -85,6 +85,9 @@ pub struct FetchNode { /// Similar to `input_rewrites`, but for optional "rewrites" to apply to the data that is /// received from a fetch (and before it is applied to the current in-memory results). pub output_rewrites: Vec>, + /// Similar to the other kinds of rewrites. This is a mechanism to convert a contextual path into + /// an argument to a resolver + pub context_rewrites: Arc>>, } #[derive(Debug, Clone)] diff --git a/apollo-federation/src/query_plan/query_planner.rs b/apollo-federation/src/query_plan/query_planner.rs index 8c04b50d3f..9d830fddcf 100644 --- a/apollo-federation/src/query_plan/query_planner.rs +++ b/apollo-federation/src/query_plan/query_planner.rs @@ -341,6 +341,7 @@ impl QueryPlanner { requires: Default::default(), input_rewrites: Default::default(), output_rewrites: Default::default(), + context_rewrites: Default::default(), }; return Ok(QueryPlan::new(node, statistics)); diff --git a/apollo-router/Cargo.toml b/apollo-router/Cargo.toml index c1d7b2f0a6..02af231321 100644 --- a/apollo-router/Cargo.toml +++ b/apollo-router/Cargo.toml @@ -65,7 +65,7 @@ askama = "0.12.1" access-json = "0.1.0" anyhow = "1.0.80" apollo-compiler.workspace = true -apollo-federation = { git = "https://github.com/apollographql/federation-next", rev = "7d25ce0d4721e3c00b2300b9377777826e159474" } +apollo-federation = { path = "../apollo-federation", version = "=0.0.11" } arc-swap = "1.6.0" async-channel = "1.9.0" async-compression = { version = "0.4.6", features = [ diff --git a/apollo-router/src/query_planner/tests.rs b/apollo-router/src/query_planner/tests.rs index 438e2a318c..62f30f20a1 100644 --- a/apollo-router/src/query_planner/tests.rs +++ b/apollo-router/src/query_planner/tests.rs @@ -1801,6 +1801,7 @@ fn broken_plan_does_not_panic() { id: Some("fetch1".into()), input_rewrites: None, output_rewrites: None, + context_rewrites: None, schema_aware_hash: Default::default(), authorization: Default::default(), }), From b2c2db4940303f232d5613bd152dc3869ece8561 Mon Sep 17 00:00:00 2001 From: Chris Lenfest Date: Mon, 6 May 2024 22:43:39 -0500 Subject: [PATCH 21/69] Updates from linter --- apollo-router/src/query_planner/convert.rs | 2 +- apollo-router/src/query_planner/fetch.rs | 22 +++++++++---------- .../snapshots/set_context__set_context.snap | 4 ++-- .../set_context__set_context_list.snap | 4 ++-- ...et_context__set_context_list_of_lists.snap | 4 ++-- ...set_context__set_context_no_typenames.snap | 4 ++-- 6 files changed, 20 insertions(+), 20 deletions(-) diff --git a/apollo-router/src/query_planner/convert.rs b/apollo-router/src/query_planner/convert.rs index 6dca972adb..e75a2474b9 100644 --- a/apollo-router/src/query_planner/convert.rs +++ b/apollo-router/src/query_planner/convert.rs @@ -155,7 +155,7 @@ impl From<&'_ next::FetchNode> for subscription::SubscriptionNode { operation_kind, input_rewrites, output_rewrites, - context_rewrites, + context_rewrites: _, } = value; Self { service_name: subgraph_name.clone(), diff --git a/apollo-router/src/query_planner/fetch.rs b/apollo-router/src/query_planner/fetch.rs index 9a036c8926..645436513c 100644 --- a/apollo-router/src/query_planner/fetch.rs +++ b/apollo-router/src/query_planner/fetch.rs @@ -10,8 +10,8 @@ use apollo_compiler::ExecutableDocument; use apollo_compiler::Node; use apollo_compiler::NodeStr; use indexmap::IndexSet; -use once_cell::sync::OnceCell as OnceLock; use json_ext::PathElement; +use once_cell::sync::OnceCell as OnceLock; use serde::Deserialize; use serde::Serialize; use tower::ServiceExt; @@ -295,10 +295,8 @@ fn query_batching_for_contextual_args2( new_variables.push(Node::new(new_variable.clone())); s = rename_variables(s, v.name.clone(), new_variable.name.clone()); - } else { - if !new_variables.iter().any(|var| var.name == v.name) { - new_variables.push(v.clone()); - } + } else if !new_variables.iter().any(|var| var.name == v.name) { + new_variables.push(v.clone()); } } @@ -459,8 +457,8 @@ impl Variables { let val = data_at_path(data, &data_path); if let Some(v) = val { // add to found - found_rewrites.insert(item.rename_key_to.clone().to_string()); - + found_rewrites + .insert(item.rename_key_to.clone().to_string()); // TODO: not great let mut new_value = v.clone(); if let Some(values) = new_value.as_array_mut() { @@ -471,7 +469,9 @@ impl Variables { &DataRewrite::KeyRenamer({ DataKeyRenamer { path: data_path.clone(), - rename_key_to: item.rename_key_to.clone(), + rename_key_to: item + .rename_key_to + .clone(), } }), ); @@ -491,11 +491,11 @@ impl Variables { return Some((item.rename_key_to.to_string(), new_value)); } } - return None; + None } DataRewrite::ValueSetter(_) => { // TODO: Log error? panic? not sure - return None; + None } } }) @@ -528,7 +528,7 @@ impl Variables { // Here we create a new map with all the key value pairs to push into variables. // Note that if all variables are the same, we just use the named parameter as a variable, but if they are different then each // entity will have it's own set of parameters all appended by _ - let (extended_vars, contextual_args) = if let Some(first_map) = named_args.get(0) { + let (extended_vars, contextual_args) = if let Some(first_map) = named_args.first() { if named_args.iter().all(|map| map == first_map) { ( first_map diff --git a/apollo-router/tests/snapshots/set_context__set_context.snap b/apollo-router/tests/snapshots/set_context__set_context.snap index 4251e6580a..3d0bad1589 100644 --- a/apollo-router/tests/snapshots/set_context__set_context.snap +++ b/apollo-router/tests/snapshots/set_context__set_context.snap @@ -31,7 +31,7 @@ expression: response "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "2a8f649a16b50733cc9918606128e13ea8e101604969209cc0df7e0306121118", + "schemaAwareHash": "d7cb2d1809789d49360ca0a60570555f83855f00547675f366915c9d9d90fef9", "authorization": { "is_authenticated": false, "scopes": [], @@ -82,7 +82,7 @@ expression: response "renameKeyTo": "Subgraph1_U_field_a" } ], - "schemaAwareHash": "cbf8f9ce0545296f3305797769f5761b58367c4b25608467fc335c9d62df8cb1", + "schemaAwareHash": "7739adeb88fa82944650f859305e1c5edf6d90c7ede9aa25fff1ebf2a28bb510", "authorization": { "is_authenticated": false, "scopes": [], diff --git a/apollo-router/tests/snapshots/set_context__set_context_list.snap b/apollo-router/tests/snapshots/set_context__set_context_list.snap index 75dfc8aacc..1daa869631 100644 --- a/apollo-router/tests/snapshots/set_context__set_context_list.snap +++ b/apollo-router/tests/snapshots/set_context__set_context_list.snap @@ -37,7 +37,7 @@ expression: response "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "9baf30a619593a77ddaf5f68d8bd58af2527c30f0d9f1161001338c0e798edf4", + "schemaAwareHash": "904cce765299d4ec167658b15259c8bc8c3093af5bb33db4d2979827472646cd", "authorization": { "is_authenticated": false, "scopes": [], @@ -89,7 +89,7 @@ expression: response "renameKeyTo": "Subgraph1_U_field_a" } ], - "schemaAwareHash": "82ac499d9905065a835b1ad324b824367a513c6eb3e3b9c0364dd6bd31edb384", + "schemaAwareHash": "2539792d01c83160bdcfaee2e8d371f56aca9083b54befb4c1aa44d011adc662", "authorization": { "is_authenticated": false, "scopes": [], diff --git a/apollo-router/tests/snapshots/set_context__set_context_list_of_lists.snap b/apollo-router/tests/snapshots/set_context__set_context_list_of_lists.snap index 3d5e713c1a..a10827fc46 100644 --- a/apollo-router/tests/snapshots/set_context__set_context_list_of_lists.snap +++ b/apollo-router/tests/snapshots/set_context__set_context_list_of_lists.snap @@ -41,7 +41,7 @@ expression: response "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "28135bcdab02ad1954997c68b030f269b89d580a697091e3397dcd058248bae0", + "schemaAwareHash": "20170491dab082e15802bd0b9e6abdf2c6803589cc969668e8b0bc8388af0542", "authorization": { "is_authenticated": false, "scopes": [], @@ -94,7 +94,7 @@ expression: response "renameKeyTo": "Subgraph1_U_field_a" } ], - "schemaAwareHash": "82ac499d9905065a835b1ad324b824367a513c6eb3e3b9c0364dd6bd31edb384", + "schemaAwareHash": "bb70779889a4cd991cc0c8931a6445e59e781b4536d1707f1d515943a3641bd8", "authorization": { "is_authenticated": false, "scopes": [], diff --git a/apollo-router/tests/snapshots/set_context__set_context_no_typenames.snap b/apollo-router/tests/snapshots/set_context__set_context_no_typenames.snap index 80147400ea..56a328e780 100644 --- a/apollo-router/tests/snapshots/set_context__set_context_no_typenames.snap +++ b/apollo-router/tests/snapshots/set_context__set_context_no_typenames.snap @@ -29,7 +29,7 @@ expression: response "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "cd8e038b3a8b1edee46de7d9f4b75d8acd76315f9ce94353f9d4d36e88fd6da7", + "schemaAwareHash": "c04be6c6131c93b4ac0d65b2ebed64669b8c74b826e1a70337f0b104932250f9", "authorization": { "is_authenticated": false, "scopes": [], @@ -80,7 +80,7 @@ expression: response "renameKeyTo": "Subgraph1_U_field_a" } ], - "schemaAwareHash": "82ac499d9905065a835b1ad324b824367a513c6eb3e3b9c0364dd6bd31edb384", + "schemaAwareHash": "2539792d01c83160bdcfaee2e8d371f56aca9083b54befb4c1aa44d011adc662", "authorization": { "is_authenticated": false, "scopes": [], From bb6bd95afc12fccc843af7c7417ae9d84fdacc32 Mon Sep 17 00:00:00 2001 From: Chris Lenfest Date: Sat, 11 May 2024 22:43:57 -0500 Subject: [PATCH 22/69] add union test and bump pin --- Cargo.lock | 12 +- apollo-router/Cargo.toml | 2 +- apollo-router/src/query_planner/fetch.rs | 13 +- .../tests/fixtures/set_context/one.json | 44 ++++- .../fixtures/set_context/supergraph.graphql | 43 +++++ apollo-router/tests/set_context.rs | 52 ++++++ ...et_context__set_context_list_of_lists.snap | 3 +- .../set_context__set_context_union.snap | 157 ++++++++++++++++++ 8 files changed, 314 insertions(+), 12 deletions(-) create mode 100644 apollo-router/tests/snapshots/set_context__set_context_union.snap diff --git a/Cargo.lock b/Cargo.lock index 55d298ea7e..65b297c30d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -360,7 +360,7 @@ dependencies = [ "regex", "reqwest", "rhai", - "router-bridge 0.5.20+v2.7.4", + "router-bridge 0.5.21+v2.7.5 (git+https://github.com/apollographql/federation-rs?rev=8e8ba0d60f99693e5cf43477261e55a9c426f9c9)", "rstack", "rust-embed", "rustls", @@ -5721,8 +5721,9 @@ dependencies = [ [[package]] name = "router-bridge" -version = "0.5.20+v2.7.4" -source = "git+https://github.com/apollographql/federation-rs?rev=499dc5d51839011093b66d89aeb8bc8ac0d9f908#499dc5d51839011093b66d89aeb8bc8ac0d9f908" +version = "0.5.21+v2.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2142445fe3fe2aae7a3c3c5083d1211a448a0dabb489a14dd90d427cf6c0b13" dependencies = [ "anyhow", "async-channel 1.9.0", @@ -5746,8 +5747,7 @@ dependencies = [ [[package]] name = "router-bridge" version = "0.5.21+v2.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2142445fe3fe2aae7a3c3c5083d1211a448a0dabb489a14dd90d427cf6c0b13" +source = "git+https://github.com/apollographql/federation-rs?rev=8e8ba0d60f99693e5cf43477261e55a9c426f9c9#8e8ba0d60f99693e5cf43477261e55a9c426f9c9" dependencies = [ "anyhow", "async-channel 1.9.0", @@ -5783,7 +5783,7 @@ dependencies = [ "libfuzzer-sys", "log", "reqwest", - "router-bridge 0.5.21+v2.7.5", + "router-bridge 0.5.21+v2.7.5 (registry+https://github.com/rust-lang/crates.io-index)", "schemars", "serde", "serde_json", diff --git a/apollo-router/Cargo.toml b/apollo-router/Cargo.toml index 02af231321..37c924bf8e 100644 --- a/apollo-router/Cargo.toml +++ b/apollo-router/Cargo.toml @@ -185,7 +185,7 @@ regex = "1.10.3" reqwest.workspace = true # note: this dependency should _always_ be pinned, prefix the version with an `=` -router-bridge = { git = "https://github.com/apollographql/federation-rs", rev = "499dc5d51839011093b66d89aeb8bc8ac0d9f908" } +router-bridge = { git = "https://github.com/apollographql/federation-rs", rev = "8e8ba0d60f99693e5cf43477261e55a9c426f9c9" } rust-embed = "8.2.0" rustls = "0.21.11" diff --git a/apollo-router/src/query_planner/fetch.rs b/apollo-router/src/query_planner/fetch.rs index 645436513c..c22aa84d62 100644 --- a/apollo-router/src/query_planner/fetch.rs +++ b/apollo-router/src/query_planner/fetch.rs @@ -369,9 +369,18 @@ fn test_query_batching_for_contextual_args() { // TODO: There is probably a function somewhere else that already does this fn data_at_path<'v>(data: &'v Value, path: &Path) -> Option<&'v Value> { - // TODO: We will have fragments with type conditions that need to be dealt with, but it's not working just yet let v = match &path.0[0] { - PathElement::Fragment(s) => data.get(s), + PathElement::Fragment(s) => { + // get the value at data.get("__typename") and compare it with s. If the values are equal, return data, otherwise None + let mut z: Option<&Value> = None; + let wrapped_typename = data.get("__typename"); + if let Some(t) = wrapped_typename { + if t.as_str() == Some(s.as_str()) { + z = Some(data); + } + } + z + }, PathElement::Key(v, _) => data.get(v), PathElement::Index(idx) => Some(&data[idx]), PathElement::Flatten(_) => None, diff --git a/apollo-router/tests/fixtures/set_context/one.json b/apollo-router/tests/fixtures/set_context/one.json index 70d5d3fc23..4902ec3976 100644 --- a/apollo-router/tests/fixtures/set_context/one.json +++ b/apollo-router/tests/fixtures/set_context/one.json @@ -97,6 +97,25 @@ } } }, + { + "request": { + "query": "query QueryUnion__Subgraph1__0{k{__typename ...on A{prop v{__typename id}}...on B{prop v{__typename id}}}}", + "operationName": "QueryUnion__Subgraph1__0" + }, + "response": { + "data": { + "k": { + "__typename": "A", + "prop": "prop value 3", + "id": 1, + "v": { + "__typename": "V", + "id": "2" + } + } + } + } + }, { "request": { "query": "query Query__Subgraph1__1($representations:[_Any!]!$Subgraph1_U_field_a:String!){_entities(representations:$representations){...on U{__typename id field(a:$Subgraph1_U_field_a)}}}", @@ -226,6 +245,27 @@ ] } } - } - ] + }, + { + "request": { + "query": "query QueryUnion__Subgraph1__1($representations:[_Any!]!$Subgraph1_V_field_a:String!){_entities(representations:$representations){...on V{id field(a:$Subgraph1_V_field_a)}}}", + "operationName": "QueryUnion__Subgraph1__1", + "variables": { + "Subgraph1_V_field_a": "prop value 3", + "representations": [ + { "__typename": "V", "id": "2" } + ] + } + }, + "response": { + "data": { + "_entities": [ + { + "id": "3", + "field": 3456 + } + ] + } + } + } ] } diff --git a/apollo-router/tests/fixtures/set_context/supergraph.graphql b/apollo-router/tests/fixtures/set_context/supergraph.graphql index 908e980668..1e2c909ed9 100644 --- a/apollo-router/tests/fixtures/set_context/supergraph.graphql +++ b/apollo-router/tests/fixtures/set_context/supergraph.graphql @@ -94,6 +94,30 @@ type Query @join__type(graph: SUBGRAPH1) @join__type(graph: SUBGRAPH2) { t: T! @join__field(graph: SUBGRAPH1) tList: [T]! @join__field(graph: SUBGRAPH1) a: Int! @join__field(graph: SUBGRAPH2) + k: K! @join__field(graph: SUBGRAPH1) +} + +union K + @join__type(graph: SUBGRAPH1) + @join__unionMember(graph: SUBGRAPH1, member: "A") + @join__unionMember(graph: SUBGRAPH1, member: "B") + @context(name: "Subgraph1__context") + = A | B + +type A + @join__type(graph: SUBGRAPH1, key: "id") +{ + id: ID! + v: V! + prop: String! +} + +type B + @join__type(graph: SUBGRAPH1, key: "id") +{ + id: ID! + v: V! + prop: String! } type T @@ -122,4 +146,23 @@ type U } ] ) + } + +type V + @join__type(graph: SUBGRAPH1, key: "id") + @join__type(graph: SUBGRAPH2, key: "id") { + id: ID! + b: String! @join__field(graph: SUBGRAPH2) + field: Int! + @join__field( + graph: SUBGRAPH1 + contextArguments: [ + { + context: "Subgraph1__context" + name: "a" + type: "String!" + selection: "... on A { prop } ... on B { prop } ... on T { prop }" + } + ] + ) } diff --git a/apollo-router/tests/set_context.rs b/apollo-router/tests/set_context.rs index d471051955..7ed213ce72 100644 --- a/apollo-router/tests/set_context.rs +++ b/apollo-router/tests/set_context.rs @@ -171,6 +171,43 @@ async fn test_set_context_list_of_lists() { insta::assert_json_snapshot!(response); } +#[tokio::test(flavor = "multi_thread")] +async fn test_set_context_union() { + let harness = setup_from_mocks( + json! {{ + "experimental_type_conditioned_fetching": true, + // will make debugging easier + "plugins": { + "experimental.expose_query_plan": true + }, + "include_subgraph_errors": { + "all": true + } + }}, + &[ + ("Subgraph1", include_str!("fixtures/set_context/one.json")), + ("Subgraph2", include_str!("fixtures/set_context/two.json")), + ], + ); + let supergraph_service = harness.build_supergraph().await.unwrap(); + let request: supergraph::Request = supergraph::Request::fake_builder() + .query(QUERY_WITH_UNION.to_string()) + .header("Apollo-Expose-Query-Plan", "true") + .variables(Default::default()) + .build() + .expect("expecting valid request"); + + let response = supergraph_service + .oneshot(request) + .await + .unwrap() + .next_response() + .await + .unwrap(); + + insta::assert_json_snapshot!(response); +} + fn setup_from_mocks( configuration: serde_json::Value, mocks: &[(&'static str, &'static str)], @@ -238,3 +275,18 @@ static QUERY_WITH_LIST_OF_LISTS: &str = r#"query QueryLL { } } }"#; + +static QUERY_WITH_UNION: &str = r#"query QueryUnion { + k { + ... on A { + v { + field + } + } + ... on B { + v { + field + } + } + } + }"#; diff --git a/apollo-router/tests/snapshots/set_context__set_context_list_of_lists.snap b/apollo-router/tests/snapshots/set_context__set_context_list_of_lists.snap index a10827fc46..e32df74999 100644 --- a/apollo-router/tests/snapshots/set_context__set_context_list_of_lists.snap +++ b/apollo-router/tests/snapshots/set_context__set_context_list_of_lists.snap @@ -51,6 +51,7 @@ expression: response { "kind": "Flatten", "path": [ + "", "tList", "@", "uList", @@ -105,7 +106,7 @@ expression: response ] } }, - "text": "QueryPlan {\n Sequence {\n Fetch(service: \"Subgraph1\") {\n {\n tList {\n prop\n id\n uList {\n __typename\n id\n }\n }\n }\n },\n Flatten(path: \"tList.@.uList.@\") {\n Fetch(service: \"Subgraph1\") {\n {\n ... on U {\n __typename\n id\n }\n } =>\n {\n ... on U {\n id\n field(a: $Subgraph1_U_field_a)\n }\n }\n },\n },\n },\n}" + "text": "QueryPlan {\n Sequence {\n Fetch(service: \"Subgraph1\") {\n {\n tList {\n prop\n id\n uList {\n __typename\n id\n }\n }\n }\n },\n Flatten(path: \".tList.@.uList.@\") {\n Fetch(service: \"Subgraph1\") {\n {\n ... on U {\n __typename\n id\n }\n } =>\n {\n ... on U {\n id\n field(a: $Subgraph1_U_field_a)\n }\n }\n },\n },\n },\n}" } } } diff --git a/apollo-router/tests/snapshots/set_context__set_context_union.snap b/apollo-router/tests/snapshots/set_context__set_context_union.snap new file mode 100644 index 0000000000..fd3e9a7b10 --- /dev/null +++ b/apollo-router/tests/snapshots/set_context__set_context_union.snap @@ -0,0 +1,157 @@ +--- +source: apollo-router/tests/set_context.rs +expression: response +--- +{ + "data": { + "k": { + "v": { + "field": 3456 + } + } + }, + "extensions": { + "apolloQueryPlan": { + "object": { + "kind": "QueryPlan", + "node": { + "kind": "Sequence", + "nodes": [ + { + "kind": "Fetch", + "serviceName": "Subgraph1", + "variableUsages": [], + "operation": "query QueryUnion__Subgraph1__0{k{__typename ...on A{prop v{__typename id}}...on B{prop v{__typename id}}}}", + "operationName": "QueryUnion__Subgraph1__0", + "operationKind": "query", + "id": null, + "inputRewrites": null, + "outputRewrites": null, + "contextRewrites": null, + "schemaAwareHash": "0f1ba6ea48b6f8adfc469d87012031090fe5de3f5ba9de8a8823a2d139c0370e", + "authorization": { + "is_authenticated": false, + "scopes": [], + "policies": [] + } + }, + { + "kind": "Parallel", + "nodes": [ + { + "kind": "Flatten", + "path": [ + "", + "k|[A]", + "v" + ], + "node": { + "kind": "Fetch", + "serviceName": "Subgraph1", + "requires": [ + { + "kind": "InlineFragment", + "typeCondition": "V", + "selections": [ + { + "kind": "Field", + "name": "__typename" + }, + { + "kind": "Field", + "name": "id" + } + ] + } + ], + "variableUsages": [ + "Subgraph1_V_field_a" + ], + "operation": "query QueryUnion__Subgraph1__1($representations:[_Any!]!$Subgraph1_V_field_a:String!){_entities(representations:$representations){...on V{id field(a:$Subgraph1_V_field_a)}}}", + "operationName": "QueryUnion__Subgraph1__1", + "operationKind": "query", + "id": null, + "inputRewrites": null, + "outputRewrites": null, + "contextRewrites": [ + { + "kind": "KeyRenamer", + "path": [ + "..", + "... on A", + "prop" + ], + "renameKeyTo": "Subgraph1_V_field_a" + } + ], + "schemaAwareHash": "3b106a3e66db579a745adf891aedc200bb97e9cef986bb740632157f556b5c0c", + "authorization": { + "is_authenticated": false, + "scopes": [], + "policies": [] + } + } + }, + { + "kind": "Flatten", + "path": [ + "", + "k|[B]", + "v" + ], + "node": { + "kind": "Fetch", + "serviceName": "Subgraph1", + "requires": [ + { + "kind": "InlineFragment", + "typeCondition": "V", + "selections": [ + { + "kind": "Field", + "name": "__typename" + }, + { + "kind": "Field", + "name": "id" + } + ] + } + ], + "variableUsages": [ + "Subgraph1_V_field_a" + ], + "operation": "query QueryUnion__Subgraph1__2($representations:[_Any!]!$Subgraph1_V_field_a:String!){_entities(representations:$representations){...on V{id field(a:$Subgraph1_V_field_a)}}}", + "operationName": "QueryUnion__Subgraph1__2", + "operationKind": "query", + "id": null, + "inputRewrites": null, + "outputRewrites": null, + "contextRewrites": [ + { + "kind": "KeyRenamer", + "path": [ + "..", + "... on B", + "prop" + ], + "renameKeyTo": "Subgraph1_V_field_a" + } + ], + "schemaAwareHash": "ed6efb6f1d6c1cdfafc000abe8f7108693e33c75a5fca5bb3e626301b0bb3d8d", + "authorization": { + "is_authenticated": false, + "scopes": [], + "policies": [] + } + } + } + ] + } + ] + } + }, + "text": "QueryPlan {\n Sequence {\n Fetch(service: \"Subgraph1\") {\n {\n k {\n __typename\n ... on A {\n prop\n v {\n __typename\n id\n }\n }\n ... on B {\n prop\n v {\n __typename\n id\n }\n }\n }\n }\n },\n Parallel {\n Flatten(path: \".k|[A].v\") {\n Fetch(service: \"Subgraph1\") {\n {\n ... on V {\n __typename\n id\n }\n } =>\n {\n ... on V {\n id\n field(a: $Subgraph1_V_field_a)\n }\n }\n },\n },\n Flatten(path: \".k|[B].v\") {\n Fetch(service: \"Subgraph1\") {\n {\n ... on V {\n __typename\n id\n }\n } =>\n {\n ... on V {\n id\n field(a: $Subgraph1_V_field_a)\n }\n }\n },\n },\n },\n },\n}" + } + } +} From 47bf117ea1fc5c8f5e2742138eaf0c77b05933a1 Mon Sep 17 00:00:00 2001 From: Chris Lenfest Date: Mon, 13 May 2024 01:15:13 -0500 Subject: [PATCH 23/69] cleaning up code --- apollo-router/src/plugin/test/mock/subgraph.rs | 1 - apollo-router/src/query_planner/execution.rs | 7 ------- apollo-router/src/query_planner/fetch.rs | 6 +++--- apollo-router/src/query_planner/subscription.rs | 2 +- 4 files changed, 4 insertions(+), 12 deletions(-) diff --git a/apollo-router/src/plugin/test/mock/subgraph.rs b/apollo-router/src/plugin/test/mock/subgraph.rs index bc156265dd..80149de1d2 100644 --- a/apollo-router/src/plugin/test/mock/subgraph.rs +++ b/apollo-router/src/plugin/test/mock/subgraph.rs @@ -173,7 +173,6 @@ impl Service for MockSubgraph { } } - dbg!(&body); let response = if let Some(response) = self.mocks.get(body) { // Build an http Response let mut http_response_builder = http::Response::builder().status(StatusCode::OK); diff --git a/apollo-router/src/query_planner/execution.rs b/apollo-router/src/query_planner/execution.rs index 4addf771a3..801069afd1 100644 --- a/apollo-router/src/query_planner/execution.rs +++ b/apollo-router/src/query_planner/execution.rs @@ -123,7 +123,6 @@ impl PlanNode { match self { PlanNode::Sequence { nodes } => { - tracing::debug!("here PlanNode::Sequence"); value = parent_value.clone(); errors = Vec::new(); async { @@ -148,7 +147,6 @@ impl PlanNode { .await } PlanNode::Parallel { nodes } => { - tracing::debug!("here PlanNode::Parallel"); value = Value::default(); errors = Vec::new(); async { @@ -177,8 +175,6 @@ impl PlanNode { .await } PlanNode::Flatten(FlattenNode { path, node }) => { - tracing::debug!("here PlanNode::FlattenNode"); - // Note that the span must be `info` as we need to pick this up in apollo tracing let current_dir = current_dir.join(path.remove_empty_key_root()); let (v, err) = node @@ -222,7 +218,6 @@ impl PlanNode { value = Value::default(); } PlanNode::Fetch(fetch_node) => { - tracing::debug!("here PlanNode::Fetch"); let fetch_time_offset = parameters.context.created_at.elapsed().as_nanos() as i64; @@ -235,11 +230,9 @@ impl PlanNode { .get::() .is_some() { - tracing::debug!("fetch node errors"); value = Value::Object(Object::default()); errors = Vec::new(); } else { - tracing::debug!("fetch node fetching"); let (v, e) = fetch_node .fetch_node(parameters, parent_value, current_dir) .instrument(tracing::info_span!( diff --git a/apollo-router/src/query_planner/fetch.rs b/apollo-router/src/query_planner/fetch.rs index c22aa84d62..535018972e 100644 --- a/apollo-router/src/query_planner/fetch.rs +++ b/apollo-router/src/query_planner/fetch.rs @@ -256,7 +256,7 @@ pub(crate) struct Variables { pub(crate) contextual_args: Option<(HashSet, usize)>, } -fn query_batching_for_contextual_args2( +fn query_batching_for_contextual_args( operation: &str, contextual_args: &Option<(HashSet, usize)>, ) -> Option { @@ -363,7 +363,7 @@ fn test_query_batching_for_contextual_args() { assert_eq!( expected, - query_batching_for_contextual_args2(old_query, &contextual_args).unwrap() + query_batching_for_contextual_args(old_query, &contextual_args).unwrap() ); } @@ -658,7 +658,7 @@ impl FetchNode { }; let query_batched_query = - query_batching_for_contextual_args2(operation.as_serialized(), &contextual_args); + query_batching_for_contextual_args(operation.as_serialized(), &contextual_args); let mut subgraph_request = SubgraphRequest::builder() .supergraph_request(parameters.supergraph_request.clone()) diff --git a/apollo-router/src/query_planner/subscription.rs b/apollo-router/src/query_planner/subscription.rs index 89db823b44..6a327fdc60 100644 --- a/apollo-router/src/query_planner/subscription.rs +++ b/apollo-router/src/query_planner/subscription.rs @@ -208,7 +208,7 @@ impl SubscriptionNode { parameters.supergraph_request, parameters.schema, &self.input_rewrites, - &self.input_rewrites, // TODO: Fix me + &None, ) { Some(variables) => variables, None => { From 8e882c3c59ab5b1ac591677ea1d11fdb1bd7f226 Mon Sep 17 00:00:00 2001 From: o0Ignition0o Date: Mon, 13 May 2024 11:26:51 +0200 Subject: [PATCH 24/69] lint --- ...dden_field_yields_expected_query_plan.snap | 1 + ...dden_field_yields_expected_query_plan.snap | 2 ++ apollo-router/src/query_planner/fetch.rs | 22 ++++++++----------- 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/apollo-router/src/plugins/progressive_override/snapshots/apollo_router__plugins__progressive_override__tests__non_overridden_field_yields_expected_query_plan.snap b/apollo-router/src/plugins/progressive_override/snapshots/apollo_router__plugins__progressive_override__tests__non_overridden_field_yields_expected_query_plan.snap index d680c0b53f..cb657dcdce 100644 --- a/apollo-router/src/plugins/progressive_override/snapshots/apollo_router__plugins__progressive_override__tests__non_overridden_field_yields_expected_query_plan.snap +++ b/apollo-router/src/plugins/progressive_override/snapshots/apollo_router__plugins__progressive_override__tests__non_overridden_field_yields_expected_query_plan.snap @@ -18,6 +18,7 @@ expression: query_plan "id": null, "inputRewrites": null, "outputRewrites": null, + "contextRewrites": null, "schemaAwareHash": "12dda6193654ae4fe6e38bc09d4f81cc73d0c9e098692096f72d2158eef4776f", "authorization": { "is_authenticated": false, diff --git a/apollo-router/src/plugins/progressive_override/snapshots/apollo_router__plugins__progressive_override__tests__overridden_field_yields_expected_query_plan.snap b/apollo-router/src/plugins/progressive_override/snapshots/apollo_router__plugins__progressive_override__tests__overridden_field_yields_expected_query_plan.snap index 612b147fc2..d18a3e2b11 100644 --- a/apollo-router/src/plugins/progressive_override/snapshots/apollo_router__plugins__progressive_override__tests__overridden_field_yields_expected_query_plan.snap +++ b/apollo-router/src/plugins/progressive_override/snapshots/apollo_router__plugins__progressive_override__tests__overridden_field_yields_expected_query_plan.snap @@ -23,6 +23,7 @@ expression: query_plan "id": null, "inputRewrites": null, "outputRewrites": null, + "contextRewrites": null, "schemaAwareHash": "00ad582ea45fc1bce436b36b21512f3d2c47b74fdbdc61e4b349289722c9ecf2", "authorization": { "is_authenticated": false, @@ -61,6 +62,7 @@ expression: query_plan "id": null, "inputRewrites": null, "outputRewrites": null, + "contextRewrites": null, "schemaAwareHash": "a8ebdc2151a2e5207882e43c6906c0c64167fd9a8e0c7c4becc47736a5105096", "authorization": { "is_authenticated": false, diff --git a/apollo-router/src/query_planner/fetch.rs b/apollo-router/src/query_planner/fetch.rs index 535018972e..09be6c3c9a 100644 --- a/apollo-router/src/query_planner/fetch.rs +++ b/apollo-router/src/query_planner/fetch.rs @@ -277,7 +277,6 @@ fn query_batching_for_contextual_args( .any(|v| ctx.contains(v.name.as_str())) { let new_selection_set: Vec<_> = (0..*times) - .into_iter() .map(|i| { // TODO: Unwrap let mut s = operation.selection_set.first().unwrap().clone(); @@ -380,7 +379,7 @@ fn data_at_path<'v>(data: &'v Value, path: &Path) -> Option<&'v Value> { } } z - }, + } PathElement::Key(v, _) => data.get(v), PathElement::Index(idx) => Some(&data[idx]), PathElement::Flatten(_) => None, @@ -401,17 +400,14 @@ fn merge_context_path(current_dir: &Path, context_path: &Path) -> Path { // iterate over the context_path(i), every time we encounter a '..', we want // to go up one level in the current_dir(j) while i < context_path.len() { - match &context_path.0[i] { - PathElement::Key(e, _) => { + match &context_path.0.get(i) { + Some(PathElement::Key(e, _)) => { let mut found = false; if e == ".." { while !found { j -= 1; - match current_dir.0[j] { - PathElement::Key(_, _) => { - found = true; - } - _ => {} + if let Some(PathElement::Key(_, _)) = current_dir.0.get(j) { + found = true; } } i += 1; @@ -423,7 +419,7 @@ fn merge_context_path(current_dir: &Path, context_path: &Path) -> Path { } } - let mut return_path: Vec = current_dir.iter().take(j).map(|e| e.clone()).collect(); + let mut return_path: Vec = current_dir.iter().take(j).cloned().collect(); context_path.iter().skip(i).for_each(|e| { return_path.push(e.clone()); @@ -462,7 +458,7 @@ impl Variables { match rewrite { DataRewrite::KeyRenamer(item) => { if !found_rewrites.contains(item.rename_key_to.as_str()) { - let data_path = merge_context_path(&path, &item.path); + let data_path = merge_context_path(path, &item.path); let val = data_at_path(data, &data_path); if let Some(v) = val { // add to found @@ -471,10 +467,10 @@ impl Variables { // TODO: not great let mut new_value = v.clone(); if let Some(values) = new_value.as_array_mut() { - for mut v in values { + for v in values { rewrites::apply_single_rewrite( schema, - &mut v, + v, &DataRewrite::KeyRenamer({ DataKeyRenamer { path: data_path.clone(), From 8c183cafed59ebec654de9d4dc195609d9103f20 Mon Sep 17 00:00:00 2001 From: o0Ignition0o Date: Mon, 13 May 2024 12:00:51 +0200 Subject: [PATCH 25/69] remove unwraps --- apollo-router/src/query_planner/fetch.rs | 47 +++++++++++++++++------- 1 file changed, 33 insertions(+), 14 deletions(-) diff --git a/apollo-router/src/query_planner/fetch.rs b/apollo-router/src/query_planner/fetch.rs index 09be6c3c9a..e3c55eb9f4 100644 --- a/apollo-router/src/query_planner/fetch.rs +++ b/apollo-router/src/query_planner/fetch.rs @@ -4,8 +4,10 @@ use std::fmt::Display; use std::sync::Arc; use apollo_compiler::ast; +use apollo_compiler::ast::Document; use apollo_compiler::ast::Name; use apollo_compiler::validation::Valid; +use apollo_compiler::validation::WithErrors; use apollo_compiler::ExecutableDocument; use apollo_compiler::Node; use apollo_compiler::NodeStr; @@ -256,15 +258,20 @@ pub(crate) struct Variables { pub(crate) contextual_args: Option<(HashSet, usize)>, } +#[derive(Debug)] +enum ContextBatchingError { + InvalidDocument(WithErrors), + NoSelectionSet, +} + fn query_batching_for_contextual_args( operation: &str, contextual_args: &Option<(HashSet, usize)>, -) -> Option { +) -> Result, ContextBatchingError> { if let Some((ctx, times)) = contextual_args { let parser = apollo_compiler::Parser::new() .parse_ast(operation, "") - // TODO: remove unwrap - .unwrap(); + .map_err(ContextBatchingError::InvalidDocument)?; if let Some(mut operation) = parser .definitions .into_iter() @@ -279,18 +286,22 @@ fn query_batching_for_contextual_args( let new_selection_set: Vec<_> = (0..*times) .map(|i| { // TODO: Unwrap - let mut s = operation.selection_set.first().unwrap().clone(); + let mut s = operation + .selection_set + .first() + .ok_or_else(|| ContextBatchingError::NoSelectionSet)? + .clone(); if let ast::Selection::Field(f) = &mut s { let f = f.make_mut(); - f.alias = Some(Name::new(format!("_{}", i)).unwrap()); + f.alias = Some(Name::new_unchecked(format!("_{}", i).into())); } for v in &operation.variables { if ctx.contains(v.name.as_str()) { let mut cloned = v.clone(); let new_variable = cloned.make_mut(); - // TODO: remove unwrap - new_variable.name = Name::new(format!("{}_{}", v.name, i)).unwrap(); + new_variable.name = + Name::new_unchecked(format!("{}_{}", v.name, i).into()); new_variables.push(Node::new(new_variable.clone())); s = rename_variables(s, v.name.clone(), new_variable.name.clone()); @@ -299,20 +310,20 @@ fn query_batching_for_contextual_args( } } - s + Ok(s) }) - .collect(); + .collect::, _>>()?; let new_operation = operation.make_mut(); new_operation.selection_set = new_selection_set; new_operation.variables = new_variables; - return Some(new_operation.serialize().no_indent().to_string()); + return Ok(Some(new_operation.serialize().no_indent().to_string())); } } } - None + Ok(None) } fn rename_variables(selection_set: ast::Selection, from: Name, to: Name) -> ast::Selection { @@ -362,7 +373,9 @@ fn test_query_batching_for_contextual_args() { assert_eq!( expected, - query_batching_for_contextual_args(old_query, &contextual_args).unwrap() + query_batching_for_contextual_args(old_query, &contextual_args) + .unwrap() + .unwrap() ); } @@ -653,8 +666,14 @@ impl FetchNode { } }; - let query_batched_query = - query_batching_for_contextual_args(operation.as_serialized(), &contextual_args); + let query_batched_query = if let Ok(qbq) = + query_batching_for_contextual_args(operation.as_serialized(), &contextual_args) + { + qbq + } else { + // TODO: do we need to provide an error here ? + return (Default::default(), Default::default()); + }; let mut subgraph_request = SubgraphRequest::builder() .supergraph_request(parameters.supergraph_request.clone()) From 55048359bf107992853fd7279c6788110e67bccf Mon Sep 17 00:00:00 2001 From: Chris Lenfest Date: Mon, 13 May 2024 09:03:46 -0500 Subject: [PATCH 26/69] change federation-rs pin --- Cargo.lock | 4 ++-- apollo-router/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 26e0db8273..b63e613a33 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -360,7 +360,7 @@ dependencies = [ "regex", "reqwest", "rhai", - "router-bridge 0.5.21+v2.7.5 (git+https://github.com/apollographql/federation-rs?rev=8e8ba0d60f99693e5cf43477261e55a9c426f9c9)", + "router-bridge 0.5.21+v2.7.5 (git+https://github.com/apollographql/federation-rs?rev=77a9e02eb540ef8dd1a69c6d9731b86de72fb8af)", "rstack", "rust-embed", "rustls", @@ -5747,7 +5747,7 @@ dependencies = [ [[package]] name = "router-bridge" version = "0.5.21+v2.7.5" -source = "git+https://github.com/apollographql/federation-rs?rev=8e8ba0d60f99693e5cf43477261e55a9c426f9c9#8e8ba0d60f99693e5cf43477261e55a9c426f9c9" +source = "git+https://github.com/apollographql/federation-rs?rev=77a9e02eb540ef8dd1a69c6d9731b86de72fb8af#77a9e02eb540ef8dd1a69c6d9731b86de72fb8af" dependencies = [ "anyhow", "async-channel 1.9.0", diff --git a/apollo-router/Cargo.toml b/apollo-router/Cargo.toml index 9c344a6b3e..33fc418a2f 100644 --- a/apollo-router/Cargo.toml +++ b/apollo-router/Cargo.toml @@ -185,7 +185,7 @@ regex = "1.10.3" reqwest.workspace = true # note: this dependency should _always_ be pinned, prefix the version with an `=` -router-bridge = { git = "https://github.com/apollographql/federation-rs", rev = "8e8ba0d60f99693e5cf43477261e55a9c426f9c9" } +router-bridge = { git = "https://github.com/apollographql/federation-rs", rev = "77a9e02eb540ef8dd1a69c6d9731b86de72fb8af" } rust-embed = "8.2.0" rustls = "0.21.11" From 0ee4eb6af681b0de3985057ef5426c569927c2e8 Mon Sep 17 00:00:00 2001 From: o0Ignition0o Date: Mon, 13 May 2024 16:49:22 +0200 Subject: [PATCH 27/69] update snapshots that now expose contextRewrites --- apollo-router/src/plugins/expose_query_plan.rs | 6 ++++-- ..._query_plan__tests__it_expose_query_plan-2.snap | 4 ++++ ...se_query_plan__tests__it_expose_query_plan.snap | 4 ++++ apollo-router/src/query_planner/fetch.rs | 14 ++++++++------ .../type_conditions__type_conditions_disabled.snap | 2 ++ .../type_conditions__type_conditions_enabled.snap | 3 +++ ...onditions_enabled_generate_query_fragments.snap | 3 +++ ...ions__type_conditions_enabled_list_of_list.snap | 3 +++ ...pe_conditions_enabled_list_of_list_of_list.snap | 3 +++ ...itions_enabled_shouldnt_make_article_fetch.snap | 3 +++ 10 files changed, 37 insertions(+), 8 deletions(-) diff --git a/apollo-router/src/plugins/expose_query_plan.rs b/apollo-router/src/plugins/expose_query_plan.rs index 78d771554c..cb339d5c3d 100644 --- a/apollo-router/src/plugins/expose_query_plan.rs +++ b/apollo-router/src/plugins/expose_query_plan.rs @@ -218,7 +218,8 @@ mod tests { build_mock_supergraph(serde_json::json! {{ "plugins": { "experimental.expose_query_plan": true - } + }, + "include_subgraph_errors": { "all": true } }}) .await, ) @@ -231,7 +232,8 @@ mod tests { build_mock_supergraph(serde_json::json! {{ "plugins": { "experimental.expose_query_plan": true - } + }, + "include_subgraph_errors": { "all": true } }}) .await, ) diff --git a/apollo-router/src/plugins/snapshots/apollo_router__plugins__expose_query_plan__tests__it_expose_query_plan-2.snap b/apollo-router/src/plugins/snapshots/apollo_router__plugins__expose_query_plan__tests__it_expose_query_plan-2.snap index b32a905c0e..0d6ab611f6 100644 --- a/apollo-router/src/plugins/snapshots/apollo_router__plugins__expose_query_plan__tests__it_expose_query_plan-2.snap +++ b/apollo-router/src/plugins/snapshots/apollo_router__plugins__expose_query_plan__tests__it_expose_query_plan-2.snap @@ -68,6 +68,7 @@ expression: "serde_json::to_value(response).unwrap()" "id": null, "inputRewrites": null, "outputRewrites": null, + "contextRewrites": null, "schemaAwareHash": "7245d488e97c3b2ac9f5fa4dd4660940b94ad81af070013305b2c0f76337b2f9", "authorization": { "is_authenticated": false, @@ -107,6 +108,7 @@ expression: "serde_json::to_value(response).unwrap()" "id": null, "inputRewrites": null, "outputRewrites": null, + "contextRewrites": null, "schemaAwareHash": "6e0b4156706ea0cf924500cfdc99dd44b9f0ed07e2d3f888d4aff156e6a33238", "authorization": { "is_authenticated": false, @@ -153,6 +155,7 @@ expression: "serde_json::to_value(response).unwrap()" "id": null, "inputRewrites": null, "outputRewrites": null, + "contextRewrites": null, "schemaAwareHash": "ff649f3d70241d5a8cd5f5d03ff4c41ecff72b0e4129a480207b05ac92318042", "authorization": { "is_authenticated": false, @@ -196,6 +199,7 @@ expression: "serde_json::to_value(response).unwrap()" "id": null, "inputRewrites": null, "outputRewrites": null, + "contextRewrites": null, "schemaAwareHash": "bf9f3beda78a7a565e47c862157bad4ec871d724d752218da1168455dddca074", "authorization": { "is_authenticated": false, diff --git a/apollo-router/src/plugins/snapshots/apollo_router__plugins__expose_query_plan__tests__it_expose_query_plan.snap b/apollo-router/src/plugins/snapshots/apollo_router__plugins__expose_query_plan__tests__it_expose_query_plan.snap index b32a905c0e..0d6ab611f6 100644 --- a/apollo-router/src/plugins/snapshots/apollo_router__plugins__expose_query_plan__tests__it_expose_query_plan.snap +++ b/apollo-router/src/plugins/snapshots/apollo_router__plugins__expose_query_plan__tests__it_expose_query_plan.snap @@ -68,6 +68,7 @@ expression: "serde_json::to_value(response).unwrap()" "id": null, "inputRewrites": null, "outputRewrites": null, + "contextRewrites": null, "schemaAwareHash": "7245d488e97c3b2ac9f5fa4dd4660940b94ad81af070013305b2c0f76337b2f9", "authorization": { "is_authenticated": false, @@ -107,6 +108,7 @@ expression: "serde_json::to_value(response).unwrap()" "id": null, "inputRewrites": null, "outputRewrites": null, + "contextRewrites": null, "schemaAwareHash": "6e0b4156706ea0cf924500cfdc99dd44b9f0ed07e2d3f888d4aff156e6a33238", "authorization": { "is_authenticated": false, @@ -153,6 +155,7 @@ expression: "serde_json::to_value(response).unwrap()" "id": null, "inputRewrites": null, "outputRewrites": null, + "contextRewrites": null, "schemaAwareHash": "ff649f3d70241d5a8cd5f5d03ff4c41ecff72b0e4129a480207b05ac92318042", "authorization": { "is_authenticated": false, @@ -196,6 +199,7 @@ expression: "serde_json::to_value(response).unwrap()" "id": null, "inputRewrites": null, "outputRewrites": null, + "contextRewrites": null, "schemaAwareHash": "bf9f3beda78a7a565e47c862157bad4ec871d724d752218da1168455dddca074", "authorization": { "is_authenticated": false, diff --git a/apollo-router/src/query_planner/fetch.rs b/apollo-router/src/query_planner/fetch.rs index e3c55eb9f4..45d73d06d4 100644 --- a/apollo-router/src/query_planner/fetch.rs +++ b/apollo-router/src/query_planner/fetch.rs @@ -456,6 +456,14 @@ impl Variables { let mut variables: serde_json_bytes::Map = Object::with_capacity(1 + variable_usages.len()); + let body = request.body(); + + variables.extend(variable_usages.iter().filter_map(|key| { + body.variables + .get_key_value(key.as_str()) + .map(|(variable_key, value)| (variable_key.clone(), value.clone())) + })); + if !requires.is_empty() { let mut inverted_paths: Vec> = Vec::new(); let mut values: IndexSet = IndexSet::new(); @@ -572,17 +580,11 @@ impl Variables { (HashMap::new(), None) }; - let body = request.body(); variables.extend( extended_vars .iter() .map(|(key, value)| (key.as_str().into(), value.clone())), ); - variables.extend(variable_usages.iter().filter_map(|key| { - body.variables - .get_key_value(key.as_str()) - .map(|(variable_key, value)| (variable_key.clone(), value.clone())) - })); variables.insert("representations", representations); Some(Variables { diff --git a/apollo-router/tests/snapshots/type_conditions__type_conditions_disabled.snap b/apollo-router/tests/snapshots/type_conditions__type_conditions_disabled.snap index e1b3c3bba7..84b137aa01 100644 --- a/apollo-router/tests/snapshots/type_conditions__type_conditions_disabled.snap +++ b/apollo-router/tests/snapshots/type_conditions__type_conditions_disabled.snap @@ -78,6 +78,7 @@ expression: response "id": null, "inputRewrites": null, "outputRewrites": null, + "contextRewrites": null, "schemaAwareHash": "0144f144d271437ed45f9d20706be86ffbf1e124d77c7add3db17d4a1498ce97", "authorization": { "is_authenticated": false, @@ -135,6 +136,7 @@ expression: response "id": null, "inputRewrites": null, "outputRewrites": null, + "contextRewrites": null, "schemaAwareHash": "23759b36e5149924c757a8b9586adec2c0f6be04ecdf2c3c3ea277446daa690b", "authorization": { "is_authenticated": false, diff --git a/apollo-router/tests/snapshots/type_conditions__type_conditions_enabled.snap b/apollo-router/tests/snapshots/type_conditions__type_conditions_enabled.snap index 49e1fef4bc..e41aeefee5 100644 --- a/apollo-router/tests/snapshots/type_conditions__type_conditions_enabled.snap +++ b/apollo-router/tests/snapshots/type_conditions__type_conditions_enabled.snap @@ -78,6 +78,7 @@ expression: response "id": null, "inputRewrites": null, "outputRewrites": null, + "contextRewrites": null, "schemaAwareHash": "0144f144d271437ed45f9d20706be86ffbf1e124d77c7add3db17d4a1498ce97", "authorization": { "is_authenticated": false, @@ -139,6 +140,7 @@ expression: response "id": null, "inputRewrites": null, "outputRewrites": null, + "contextRewrites": null, "schemaAwareHash": "23759b36e5149924c757a8b9586adec2c0f6be04ecdf2c3c3ea277446daa690b", "authorization": { "is_authenticated": false, @@ -198,6 +200,7 @@ expression: response "id": null, "inputRewrites": null, "outputRewrites": null, + "contextRewrites": null, "schemaAwareHash": "8ee58ad8b4823bcbda9126d2565e1cb04bf91ff250b1098476a1d7614a870121", "authorization": { "is_authenticated": false, diff --git a/apollo-router/tests/snapshots/type_conditions__type_conditions_enabled_generate_query_fragments.snap b/apollo-router/tests/snapshots/type_conditions__type_conditions_enabled_generate_query_fragments.snap index b04c2208ec..d92517b39d 100644 --- a/apollo-router/tests/snapshots/type_conditions__type_conditions_enabled_generate_query_fragments.snap +++ b/apollo-router/tests/snapshots/type_conditions__type_conditions_enabled_generate_query_fragments.snap @@ -78,6 +78,7 @@ expression: response "id": null, "inputRewrites": null, "outputRewrites": null, + "contextRewrites": null, "schemaAwareHash": "844dc4e409cdca1334abe37c347bd4e330123078dd7e65bda8dbb57ea5bdf59c", "authorization": { "is_authenticated": false, @@ -139,6 +140,7 @@ expression: response "id": null, "inputRewrites": null, "outputRewrites": null, + "contextRewrites": null, "schemaAwareHash": "ad82ce0af279c6a012d6b349ff823ba1467902223312aed1cdfc494ec3100b3e", "authorization": { "is_authenticated": false, @@ -198,6 +200,7 @@ expression: response "id": null, "inputRewrites": null, "outputRewrites": null, + "contextRewrites": null, "schemaAwareHash": "7c267302cf4a44a4463820237830155ab50be32c8860371d8a5c8ca905476360", "authorization": { "is_authenticated": false, diff --git a/apollo-router/tests/snapshots/type_conditions__type_conditions_enabled_list_of_list.snap b/apollo-router/tests/snapshots/type_conditions__type_conditions_enabled_list_of_list.snap index 45697537d8..acffc62599 100644 --- a/apollo-router/tests/snapshots/type_conditions__type_conditions_enabled_list_of_list.snap +++ b/apollo-router/tests/snapshots/type_conditions__type_conditions_enabled_list_of_list.snap @@ -140,6 +140,7 @@ expression: response "id": null, "inputRewrites": null, "outputRewrites": null, + "contextRewrites": null, "schemaAwareHash": "1343b4972ec8be54afe990c69711ce790992a814f9654e34e2ee2b25e4097e45", "authorization": { "is_authenticated": false, @@ -202,6 +203,7 @@ expression: response "id": null, "inputRewrites": null, "outputRewrites": null, + "contextRewrites": null, "schemaAwareHash": "23759b36e5149924c757a8b9586adec2c0f6be04ecdf2c3c3ea277446daa690b", "authorization": { "is_authenticated": false, @@ -262,6 +264,7 @@ expression: response "id": null, "inputRewrites": null, "outputRewrites": null, + "contextRewrites": null, "schemaAwareHash": "8ee58ad8b4823bcbda9126d2565e1cb04bf91ff250b1098476a1d7614a870121", "authorization": { "is_authenticated": false, diff --git a/apollo-router/tests/snapshots/type_conditions__type_conditions_enabled_list_of_list_of_list.snap b/apollo-router/tests/snapshots/type_conditions__type_conditions_enabled_list_of_list_of_list.snap index 528a658fe6..2b8feaafc3 100644 --- a/apollo-router/tests/snapshots/type_conditions__type_conditions_enabled_list_of_list_of_list.snap +++ b/apollo-router/tests/snapshots/type_conditions__type_conditions_enabled_list_of_list_of_list.snap @@ -144,6 +144,7 @@ expression: response "id": null, "inputRewrites": null, "outputRewrites": null, + "contextRewrites": null, "schemaAwareHash": "3698f4e74ead34f43a949e1e8459850337a1a07245f8ed627b9203904b4cfff4", "authorization": { "is_authenticated": false, @@ -207,6 +208,7 @@ expression: response "id": null, "inputRewrites": null, "outputRewrites": null, + "contextRewrites": null, "schemaAwareHash": "23759b36e5149924c757a8b9586adec2c0f6be04ecdf2c3c3ea277446daa690b", "authorization": { "is_authenticated": false, @@ -268,6 +270,7 @@ expression: response "id": null, "inputRewrites": null, "outputRewrites": null, + "contextRewrites": null, "schemaAwareHash": "8ee58ad8b4823bcbda9126d2565e1cb04bf91ff250b1098476a1d7614a870121", "authorization": { "is_authenticated": false, diff --git a/apollo-router/tests/snapshots/type_conditions__type_conditions_enabled_shouldnt_make_article_fetch.snap b/apollo-router/tests/snapshots/type_conditions__type_conditions_enabled_shouldnt_make_article_fetch.snap index 38ef6bc51a..5020d447b4 100644 --- a/apollo-router/tests/snapshots/type_conditions__type_conditions_enabled_shouldnt_make_article_fetch.snap +++ b/apollo-router/tests/snapshots/type_conditions__type_conditions_enabled_shouldnt_make_article_fetch.snap @@ -53,6 +53,7 @@ expression: response "id": null, "inputRewrites": null, "outputRewrites": null, + "contextRewrites": null, "schemaAwareHash": "0144f144d271437ed45f9d20706be86ffbf1e124d77c7add3db17d4a1498ce97", "authorization": { "is_authenticated": false, @@ -114,6 +115,7 @@ expression: response "id": null, "inputRewrites": null, "outputRewrites": null, + "contextRewrites": null, "schemaAwareHash": "23759b36e5149924c757a8b9586adec2c0f6be04ecdf2c3c3ea277446daa690b", "authorization": { "is_authenticated": false, @@ -173,6 +175,7 @@ expression: response "id": null, "inputRewrites": null, "outputRewrites": null, + "contextRewrites": null, "schemaAwareHash": "8ee58ad8b4823bcbda9126d2565e1cb04bf91ff250b1098476a1d7614a870121", "authorization": { "is_authenticated": false, From 937622633e65c3a9788ded8b065f5d3455fcb768 Mon Sep 17 00:00:00 2001 From: Chris Lenfest Date: Mon, 13 May 2024 10:22:21 -0500 Subject: [PATCH 28/69] updating snapshots for latest version of federation --- .../tests/fixtures/set_context/one.json | 28 +++++++++---------- .../snapshots/set_context__set_context.snap | 11 ++++---- .../set_context__set_context_list.snap | 11 ++++---- ...et_context__set_context_list_of_lists.snap | 10 +++---- ...set_context__set_context_no_typenames.snap | 11 ++++---- .../set_context__set_context_union.snap | 18 ++++++------ 6 files changed, 46 insertions(+), 43 deletions(-) diff --git a/apollo-router/tests/fixtures/set_context/one.json b/apollo-router/tests/fixtures/set_context/one.json index 4902ec3976..e03720c4d5 100644 --- a/apollo-router/tests/fixtures/set_context/one.json +++ b/apollo-router/tests/fixtures/set_context/one.json @@ -118,10 +118,10 @@ }, { "request": { - "query": "query Query__Subgraph1__1($representations:[_Any!]!$Subgraph1_U_field_a:String!){_entities(representations:$representations){...on U{__typename id field(a:$Subgraph1_U_field_a)}}}", + "query": "query Query__Subgraph1__1($representations:[_Any!]!$contextualArgument_1_0:String!){_entities(representations:$representations){...on U{__typename field(a:$contextualArgument_1_0)}}}", "operationName": "Query__Subgraph1__1", "variables": { - "Subgraph1_U_field_a": "prop value", + "contextualArgument_1_0": "prop value", "representations": [{ "__typename": "U", "id": "1" }] } }, @@ -139,10 +139,10 @@ }, { "request": { - "query": "query Query__Subgraph1__1($representations:[_Any!]!$Subgraph1_U_field_a:String!){_entities(representations:$representations){...on U{id field(a:$Subgraph1_U_field_a)}}}", + "query": "query Query__Subgraph1__1($representations:[_Any!]!$contextualArgument_1_0:String!){_entities(representations:$representations){...on U{field(a:$contextualArgument_1_0)}}}", "operationName": "Query__Subgraph1__1", "variables": { - "Subgraph1_U_field_a": "prop value", + "contextualArgument_1_0": "prop value", "representations": [{ "__typename": "U", "id": "1" }] } }, @@ -160,10 +160,10 @@ }, { "request": { - "query": "query Query__Subgraph1__1($representations:[_Any!]!$Subgraph1_U_field_a:String!){_entities(representations:$representations){...on U{id field(a:$Subgraph1_U_field_a)}}}", + "query": "query Query__Subgraph1__1($representations:[_Any!]!$contextualArgument_1_0:String!){_entities(representations:$representations){...on U{field(a:$contextualArgument_1_0)}}}", "operationName": "Query__Subgraph1__1", "variables": { - "Subgraph1_U_field_a": "prop value", + "contextualArgument_1_0": "prop value", "representations": [ { "__typename": "U", "id": "1" }, { "__typename": "U", "id": "2" }, @@ -192,11 +192,11 @@ }, { "request": { - "query": "query QueryLL__Subgraph1__1($representations: [_Any!]!, $Subgraph1_U_field_a_0: String!, $Subgraph1_U_field_a_1: String!) { _0: _entities(representations: $representations) { ... on U { id field(a: $Subgraph1_U_field_a_0) } } _1: _entities(representations: $representations) { ... on U { id field(a: $Subgraph1_U_field_a_1) } } }", + "query": "query QueryLL__Subgraph1__1($representations: [_Any!]!, $contextualArgument_1_0_0: String!, $contextualArgument_1_0_1: String!) { _0: _entities(representations: $representations) { ... on U { field(a: $contextualArgument_1_0_0) } } _1: _entities(representations: $representations) { ... on U { field(a: $contextualArgument_1_0_1) } } }", "operationName": "QueryLL__Subgraph1__1", "variables": { - "Subgraph1_U_field_a_0": "prop value 1", - "Subgraph1_U_field_a_1": "prop value 2", + "contextualArgument_1_0_0": "prop value 1", + "contextualArgument_1_0_1": "prop value 2", "representations": [ { "__typename": "U", "id": "3" }, { "__typename": "U", "id": "4" } @@ -220,11 +220,11 @@ }, { "request": { - "query": "query QueryLL__Subgraph1__1($representations: [_Any!]!, $Subgraph1_U_field_a_0: String!, $Subgraph1_U_field_a_1: String!) { _0: _entities(representations: $representations) { ... on U { id field(a: $Subgraph1_U_field_a_0) } } _1: _entities(representations: $representations) { ... on U { id field(a: $Subgraph1_U_field_a_1) } } }", + "query": "query QueryLL__Subgraph1__1($representations: [_Any!]!, $contextualArgument_1_0_0: String!, $contextualArgument_1_0_1: String!) { _0: _entities(representations: $representations) { ... on U { field(a: $contextualArgument_1_0_0) } } _1: _entities(representations: $representations) { ... on U { field(a: $contextualArgument_1_0_1) } } }", "operationName": "QueryLL__Subgraph1__1", "variables": { - "Subgraph1_U_field_a_1": "prop value 2", - "Subgraph1_U_field_a_0": "prop value 1", + "contextualArgument_1_0_1": "prop value 2", + "contextualArgument_1_0_0": "prop value 1", "representations": [ { "__typename": "U", "id": "3" }, { "__typename": "U", "id": "4" } @@ -248,10 +248,10 @@ }, { "request": { - "query": "query QueryUnion__Subgraph1__1($representations:[_Any!]!$Subgraph1_V_field_a:String!){_entities(representations:$representations){...on V{id field(a:$Subgraph1_V_field_a)}}}", + "query": "query QueryUnion__Subgraph1__1($representations:[_Any!]!$contextualArgument_1_1:String!){_entities(representations:$representations){...on V{field(a:$contextualArgument_1_1)}}}", "operationName": "QueryUnion__Subgraph1__1", "variables": { - "Subgraph1_V_field_a": "prop value 3", + "contextualArgument_1_1": "prop value 3", "representations": [ { "__typename": "V", "id": "2" } ] diff --git a/apollo-router/tests/snapshots/set_context__set_context.snap b/apollo-router/tests/snapshots/set_context__set_context.snap index 3d0bad1589..17c6ede366 100644 --- a/apollo-router/tests/snapshots/set_context__set_context.snap +++ b/apollo-router/tests/snapshots/set_context__set_context.snap @@ -41,6 +41,7 @@ expression: response { "kind": "Flatten", "path": [ + "", "t", "u" ], @@ -64,9 +65,9 @@ expression: response } ], "variableUsages": [ - "Subgraph1_U_field_a" + "contextualArgument_1_0" ], - "operation": "query Query__Subgraph1__1($representations:[_Any!]!$Subgraph1_U_field_a:String!){_entities(representations:$representations){...on U{__typename id field(a:$Subgraph1_U_field_a)}}}", + "operation": "query Query__Subgraph1__1($representations:[_Any!]!$contextualArgument_1_0:String!){_entities(representations:$representations){...on U{__typename field(a:$contextualArgument_1_0)}}}", "operationName": "Query__Subgraph1__1", "operationKind": "query", "id": null, @@ -79,10 +80,10 @@ expression: response "..", "prop" ], - "renameKeyTo": "Subgraph1_U_field_a" + "renameKeyTo": "contextualArgument_1_0" } ], - "schemaAwareHash": "7739adeb88fa82944650f859305e1c5edf6d90c7ede9aa25fff1ebf2a28bb510", + "schemaAwareHash": "ef0e5caa045bd7e162c6003f2152476e70e017aff6e925857fa2a2def13f11d4", "authorization": { "is_authenticated": false, "scopes": [], @@ -93,7 +94,7 @@ expression: response ] } }, - "text": "QueryPlan {\n Sequence {\n Fetch(service: \"Subgraph1\") {\n {\n t {\n __typename\n prop\n id\n u {\n __typename\n id\n }\n }\n }\n },\n Flatten(path: \"t.u\") {\n Fetch(service: \"Subgraph1\") {\n {\n ... on U {\n __typename\n id\n }\n } =>\n {\n ... on U {\n __typename\n id\n field(a: $Subgraph1_U_field_a)\n }\n }\n },\n },\n },\n}" + "text": "QueryPlan {\n Sequence {\n Fetch(service: \"Subgraph1\") {\n {\n t {\n __typename\n prop\n id\n u {\n __typename\n id\n }\n }\n }\n },\n Flatten(path: \".t.u\") {\n Fetch(service: \"Subgraph1\") {\n {\n ... on U {\n __typename\n id\n }\n } =>\n {\n ... on U {\n __typename\n field(a: $contextualArgument_1_0)\n }\n }\n },\n },\n },\n}" } } } diff --git a/apollo-router/tests/snapshots/set_context__set_context_list.snap b/apollo-router/tests/snapshots/set_context__set_context_list.snap index 1daa869631..bba9eb03c3 100644 --- a/apollo-router/tests/snapshots/set_context__set_context_list.snap +++ b/apollo-router/tests/snapshots/set_context__set_context_list.snap @@ -47,6 +47,7 @@ expression: response { "kind": "Flatten", "path": [ + "", "t", "uList", "@" @@ -71,9 +72,9 @@ expression: response } ], "variableUsages": [ - "Subgraph1_U_field_a" + "contextualArgument_1_0" ], - "operation": "query Query__Subgraph1__1($representations:[_Any!]!$Subgraph1_U_field_a:String!){_entities(representations:$representations){...on U{id field(a:$Subgraph1_U_field_a)}}}", + "operation": "query Query__Subgraph1__1($representations:[_Any!]!$contextualArgument_1_0:String!){_entities(representations:$representations){...on U{field(a:$contextualArgument_1_0)}}}", "operationName": "Query__Subgraph1__1", "operationKind": "query", "id": null, @@ -86,10 +87,10 @@ expression: response "..", "prop" ], - "renameKeyTo": "Subgraph1_U_field_a" + "renameKeyTo": "contextualArgument_1_0" } ], - "schemaAwareHash": "2539792d01c83160bdcfaee2e8d371f56aca9083b54befb4c1aa44d011adc662", + "schemaAwareHash": "c3941670991c332a40e1b0fc3b6d01a04211e2341f1a36a2c42bee4541d985e4", "authorization": { "is_authenticated": false, "scopes": [], @@ -100,7 +101,7 @@ expression: response ] } }, - "text": "QueryPlan {\n Sequence {\n Fetch(service: \"Subgraph1\") {\n {\n t {\n prop\n id\n uList {\n __typename\n id\n }\n }\n }\n },\n Flatten(path: \"t.uList.@\") {\n Fetch(service: \"Subgraph1\") {\n {\n ... on U {\n __typename\n id\n }\n } =>\n {\n ... on U {\n id\n field(a: $Subgraph1_U_field_a)\n }\n }\n },\n },\n },\n}" + "text": "QueryPlan {\n Sequence {\n Fetch(service: \"Subgraph1\") {\n {\n t {\n prop\n id\n uList {\n __typename\n id\n }\n }\n }\n },\n Flatten(path: \".t.uList.@\") {\n Fetch(service: \"Subgraph1\") {\n {\n ... on U {\n __typename\n id\n }\n } =>\n {\n ... on U {\n field(a: $contextualArgument_1_0)\n }\n }\n },\n },\n },\n}" } } } diff --git a/apollo-router/tests/snapshots/set_context__set_context_list_of_lists.snap b/apollo-router/tests/snapshots/set_context__set_context_list_of_lists.snap index e32df74999..4edde5aec2 100644 --- a/apollo-router/tests/snapshots/set_context__set_context_list_of_lists.snap +++ b/apollo-router/tests/snapshots/set_context__set_context_list_of_lists.snap @@ -77,9 +77,9 @@ expression: response } ], "variableUsages": [ - "Subgraph1_U_field_a" + "contextualArgument_1_0" ], - "operation": "query QueryLL__Subgraph1__1($representations:[_Any!]!$Subgraph1_U_field_a:String!){_entities(representations:$representations){...on U{id field(a:$Subgraph1_U_field_a)}}}", + "operation": "query QueryLL__Subgraph1__1($representations:[_Any!]!$contextualArgument_1_0:String!){_entities(representations:$representations){...on U{field(a:$contextualArgument_1_0)}}}", "operationName": "QueryLL__Subgraph1__1", "operationKind": "query", "id": null, @@ -92,10 +92,10 @@ expression: response "..", "prop" ], - "renameKeyTo": "Subgraph1_U_field_a" + "renameKeyTo": "contextualArgument_1_0" } ], - "schemaAwareHash": "bb70779889a4cd991cc0c8931a6445e59e781b4536d1707f1d515943a3641bd8", + "schemaAwareHash": "1b34ea844758738c6cd197900b48a6a3717db97d30a43c77faf63b3a39f84dda", "authorization": { "is_authenticated": false, "scopes": [], @@ -106,7 +106,7 @@ expression: response ] } }, - "text": "QueryPlan {\n Sequence {\n Fetch(service: \"Subgraph1\") {\n {\n tList {\n prop\n id\n uList {\n __typename\n id\n }\n }\n }\n },\n Flatten(path: \".tList.@.uList.@\") {\n Fetch(service: \"Subgraph1\") {\n {\n ... on U {\n __typename\n id\n }\n } =>\n {\n ... on U {\n id\n field(a: $Subgraph1_U_field_a)\n }\n }\n },\n },\n },\n}" + "text": "QueryPlan {\n Sequence {\n Fetch(service: \"Subgraph1\") {\n {\n tList {\n prop\n id\n uList {\n __typename\n id\n }\n }\n }\n },\n Flatten(path: \".tList.@.uList.@\") {\n Fetch(service: \"Subgraph1\") {\n {\n ... on U {\n __typename\n id\n }\n } =>\n {\n ... on U {\n field(a: $contextualArgument_1_0)\n }\n }\n },\n },\n },\n}" } } } diff --git a/apollo-router/tests/snapshots/set_context__set_context_no_typenames.snap b/apollo-router/tests/snapshots/set_context__set_context_no_typenames.snap index 56a328e780..0d0a17919b 100644 --- a/apollo-router/tests/snapshots/set_context__set_context_no_typenames.snap +++ b/apollo-router/tests/snapshots/set_context__set_context_no_typenames.snap @@ -39,6 +39,7 @@ expression: response { "kind": "Flatten", "path": [ + "", "t", "u" ], @@ -62,9 +63,9 @@ expression: response } ], "variableUsages": [ - "Subgraph1_U_field_a" + "contextualArgument_1_0" ], - "operation": "query Query__Subgraph1__1($representations:[_Any!]!$Subgraph1_U_field_a:String!){_entities(representations:$representations){...on U{id field(a:$Subgraph1_U_field_a)}}}", + "operation": "query Query__Subgraph1__1($representations:[_Any!]!$contextualArgument_1_0:String!){_entities(representations:$representations){...on U{field(a:$contextualArgument_1_0)}}}", "operationName": "Query__Subgraph1__1", "operationKind": "query", "id": null, @@ -77,10 +78,10 @@ expression: response "..", "prop" ], - "renameKeyTo": "Subgraph1_U_field_a" + "renameKeyTo": "contextualArgument_1_0" } ], - "schemaAwareHash": "2539792d01c83160bdcfaee2e8d371f56aca9083b54befb4c1aa44d011adc662", + "schemaAwareHash": "c3941670991c332a40e1b0fc3b6d01a04211e2341f1a36a2c42bee4541d985e4", "authorization": { "is_authenticated": false, "scopes": [], @@ -91,7 +92,7 @@ expression: response ] } }, - "text": "QueryPlan {\n Sequence {\n Fetch(service: \"Subgraph1\") {\n {\n t {\n prop\n id\n u {\n __typename\n id\n }\n }\n }\n },\n Flatten(path: \"t.u\") {\n Fetch(service: \"Subgraph1\") {\n {\n ... on U {\n __typename\n id\n }\n } =>\n {\n ... on U {\n id\n field(a: $Subgraph1_U_field_a)\n }\n }\n },\n },\n },\n}" + "text": "QueryPlan {\n Sequence {\n Fetch(service: \"Subgraph1\") {\n {\n t {\n prop\n id\n u {\n __typename\n id\n }\n }\n }\n },\n Flatten(path: \".t.u\") {\n Fetch(service: \"Subgraph1\") {\n {\n ... on U {\n __typename\n id\n }\n } =>\n {\n ... on U {\n field(a: $contextualArgument_1_0)\n }\n }\n },\n },\n },\n}" } } } diff --git a/apollo-router/tests/snapshots/set_context__set_context_union.snap b/apollo-router/tests/snapshots/set_context__set_context_union.snap index fd3e9a7b10..4b1a41c4b4 100644 --- a/apollo-router/tests/snapshots/set_context__set_context_union.snap +++ b/apollo-router/tests/snapshots/set_context__set_context_union.snap @@ -65,9 +65,9 @@ expression: response } ], "variableUsages": [ - "Subgraph1_V_field_a" + "contextualArgument_1_1" ], - "operation": "query QueryUnion__Subgraph1__1($representations:[_Any!]!$Subgraph1_V_field_a:String!){_entities(representations:$representations){...on V{id field(a:$Subgraph1_V_field_a)}}}", + "operation": "query QueryUnion__Subgraph1__1($representations:[_Any!]!$contextualArgument_1_1:String!){_entities(representations:$representations){...on V{field(a:$contextualArgument_1_1)}}}", "operationName": "QueryUnion__Subgraph1__1", "operationKind": "query", "id": null, @@ -81,10 +81,10 @@ expression: response "... on A", "prop" ], - "renameKeyTo": "Subgraph1_V_field_a" + "renameKeyTo": "contextualArgument_1_1" } ], - "schemaAwareHash": "3b106a3e66db579a745adf891aedc200bb97e9cef986bb740632157f556b5c0c", + "schemaAwareHash": "9ecc4a9a1ef31dd054ca44d1fa6cf12ca2198604116f40eb5a2dd0411454ce47", "authorization": { "is_authenticated": false, "scopes": [], @@ -119,9 +119,9 @@ expression: response } ], "variableUsages": [ - "Subgraph1_V_field_a" + "contextualArgument_1_1" ], - "operation": "query QueryUnion__Subgraph1__2($representations:[_Any!]!$Subgraph1_V_field_a:String!){_entities(representations:$representations){...on V{id field(a:$Subgraph1_V_field_a)}}}", + "operation": "query QueryUnion__Subgraph1__2($representations:[_Any!]!$contextualArgument_1_1:String!){_entities(representations:$representations){...on V{field(a:$contextualArgument_1_1)}}}", "operationName": "QueryUnion__Subgraph1__2", "operationKind": "query", "id": null, @@ -135,10 +135,10 @@ expression: response "... on B", "prop" ], - "renameKeyTo": "Subgraph1_V_field_a" + "renameKeyTo": "contextualArgument_1_1" } ], - "schemaAwareHash": "ed6efb6f1d6c1cdfafc000abe8f7108693e33c75a5fca5bb3e626301b0bb3d8d", + "schemaAwareHash": "0a393294385fd932d1123d1fe054146f9fcd25dd927b02a72beecb2d6c3d867e", "authorization": { "is_authenticated": false, "scopes": [], @@ -151,7 +151,7 @@ expression: response ] } }, - "text": "QueryPlan {\n Sequence {\n Fetch(service: \"Subgraph1\") {\n {\n k {\n __typename\n ... on A {\n prop\n v {\n __typename\n id\n }\n }\n ... on B {\n prop\n v {\n __typename\n id\n }\n }\n }\n }\n },\n Parallel {\n Flatten(path: \".k|[A].v\") {\n Fetch(service: \"Subgraph1\") {\n {\n ... on V {\n __typename\n id\n }\n } =>\n {\n ... on V {\n id\n field(a: $Subgraph1_V_field_a)\n }\n }\n },\n },\n Flatten(path: \".k|[B].v\") {\n Fetch(service: \"Subgraph1\") {\n {\n ... on V {\n __typename\n id\n }\n } =>\n {\n ... on V {\n id\n field(a: $Subgraph1_V_field_a)\n }\n }\n },\n },\n },\n },\n}" + "text": "QueryPlan {\n Sequence {\n Fetch(service: \"Subgraph1\") {\n {\n k {\n __typename\n ... on A {\n prop\n v {\n __typename\n id\n }\n }\n ... on B {\n prop\n v {\n __typename\n id\n }\n }\n }\n }\n },\n Parallel {\n Flatten(path: \".k|[A].v\") {\n Fetch(service: \"Subgraph1\") {\n {\n ... on V {\n __typename\n id\n }\n } =>\n {\n ... on V {\n field(a: $contextualArgument_1_1)\n }\n }\n },\n },\n Flatten(path: \".k|[B].v\") {\n Fetch(service: \"Subgraph1\") {\n {\n ... on V {\n __typename\n id\n }\n } =>\n {\n ... on V {\n field(a: $contextualArgument_1_1)\n }\n }\n },\n },\n },\n },\n}" } } } From 35aca9f1c09e75e57752cb217141f02eaedf1dac Mon Sep 17 00:00:00 2001 From: Chris Lenfest Date: Wed, 15 May 2024 17:08:21 -0500 Subject: [PATCH 29/69] checkpoint --- Cargo.lock | 4 +- apollo-federation/src/query_plan/mod.rs | 2 +- apollo-router/Cargo.toml | 2 +- .../src/plugins/expose_query_plan.rs | 6 +- apollo-router/src/query_planner/fetch.rs | 342 ++---------- apollo-router/src/query_planner/mod.rs | 1 + .../src/query_planner/subgraph_context.rs | 503 ++++++++++++++++++ .../tests/fixtures/set_context/one.json | 5 +- .../fixtures/set_context/supergraph.graphql | 14 +- .../set_context__set_context.snap.new | 102 ++++ ...ontext__set_context_list_of_lists.snap.new | 138 +++++ 11 files changed, 789 insertions(+), 330 deletions(-) create mode 100644 apollo-router/src/query_planner/subgraph_context.rs create mode 100644 apollo-router/tests/snapshots/set_context__set_context.snap.new create mode 100644 apollo-router/tests/snapshots/set_context__set_context_list_of_lists.snap.new diff --git a/Cargo.lock b/Cargo.lock index b63e613a33..a35ceb3cdd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -360,7 +360,7 @@ dependencies = [ "regex", "reqwest", "rhai", - "router-bridge 0.5.21+v2.7.5 (git+https://github.com/apollographql/federation-rs?rev=77a9e02eb540ef8dd1a69c6d9731b86de72fb8af)", + "router-bridge 0.5.21+v2.7.5 (git+https://github.com/apollographql/federation-rs?rev=518ce143088f84575e2c64436985802a25458a13)", "rstack", "rust-embed", "rustls", @@ -5747,7 +5747,7 @@ dependencies = [ [[package]] name = "router-bridge" version = "0.5.21+v2.7.5" -source = "git+https://github.com/apollographql/federation-rs?rev=77a9e02eb540ef8dd1a69c6d9731b86de72fb8af#77a9e02eb540ef8dd1a69c6d9731b86de72fb8af" +source = "git+https://github.com/apollographql/federation-rs?rev=518ce143088f84575e2c64436985802a25458a13#518ce143088f84575e2c64436985802a25458a13" dependencies = [ "anyhow", "async-channel 1.9.0", diff --git a/apollo-federation/src/query_plan/mod.rs b/apollo-federation/src/query_plan/mod.rs index eb7ffe5085..6c96fa844a 100644 --- a/apollo-federation/src/query_plan/mod.rs +++ b/apollo-federation/src/query_plan/mod.rs @@ -87,7 +87,7 @@ pub struct FetchNode { pub output_rewrites: Vec>, /// Similar to the other kinds of rewrites. This is a mechanism to convert a contextual path into /// an argument to a resolver - pub context_rewrites: Arc>>, + pub context_rewrites: Vec>, } #[derive(Debug, Clone)] diff --git a/apollo-router/Cargo.toml b/apollo-router/Cargo.toml index 33fc418a2f..786f5bce19 100644 --- a/apollo-router/Cargo.toml +++ b/apollo-router/Cargo.toml @@ -185,7 +185,7 @@ regex = "1.10.3" reqwest.workspace = true # note: this dependency should _always_ be pinned, prefix the version with an `=` -router-bridge = { git = "https://github.com/apollographql/federation-rs", rev = "77a9e02eb540ef8dd1a69c6d9731b86de72fb8af" } +router-bridge = { git = "https://github.com/apollographql/federation-rs", rev = "518ce143088f84575e2c64436985802a25458a13" } rust-embed = "8.2.0" rustls = "0.21.11" diff --git a/apollo-router/src/plugins/expose_query_plan.rs b/apollo-router/src/plugins/expose_query_plan.rs index cb339d5c3d..78d771554c 100644 --- a/apollo-router/src/plugins/expose_query_plan.rs +++ b/apollo-router/src/plugins/expose_query_plan.rs @@ -218,8 +218,7 @@ mod tests { build_mock_supergraph(serde_json::json! {{ "plugins": { "experimental.expose_query_plan": true - }, - "include_subgraph_errors": { "all": true } + } }}) .await, ) @@ -232,8 +231,7 @@ mod tests { build_mock_supergraph(serde_json::json! {{ "plugins": { "experimental.expose_query_plan": true - }, - "include_subgraph_errors": { "all": true } + } }}) .await, ) diff --git a/apollo-router/src/query_planner/fetch.rs b/apollo-router/src/query_planner/fetch.rs index 45d73d06d4..a06cad2d23 100644 --- a/apollo-router/src/query_planner/fetch.rs +++ b/apollo-router/src/query_planner/fetch.rs @@ -1,18 +1,12 @@ use std::collections::HashMap; -use std::collections::HashSet; use std::fmt::Display; use std::sync::Arc; use apollo_compiler::ast; -use apollo_compiler::ast::Document; -use apollo_compiler::ast::Name; use apollo_compiler::validation::Valid; -use apollo_compiler::validation::WithErrors; use apollo_compiler::ExecutableDocument; -use apollo_compiler::Node; use apollo_compiler::NodeStr; use indexmap::IndexSet; -use json_ext::PathElement; use once_cell::sync::OnceCell as OnceLock; use serde::Deserialize; use serde::Serialize; @@ -22,10 +16,10 @@ use tracing::Instrument; use super::execution::ExecutionParameters; use super::rewrites; -use super::rewrites::DataKeyRenamer; -use super::rewrites::DataRewrite; use super::selection::execute_selection_set; use super::selection::Selection; +use super::subgraph_context::build_operation_with_aliasing; +use super::subgraph_context::ContextualArguments; use crate::error::Error; use crate::error::FetchError; use crate::error::ValidationErrors; @@ -43,6 +37,8 @@ use crate::services::SubgraphRequest; use crate::spec::query::change::QueryHashVisitor; use crate::spec::Schema; +use crate::query_planner::subgraph_context::SubgraphContext; + /// GraphQL operation type. #[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] @@ -255,189 +251,7 @@ impl Display for QueryHash { pub(crate) struct Variables { pub(crate) variables: Object, pub(crate) inverted_paths: Vec>, - pub(crate) contextual_args: Option<(HashSet, usize)>, -} - -#[derive(Debug)] -enum ContextBatchingError { - InvalidDocument(WithErrors), - NoSelectionSet, -} - -fn query_batching_for_contextual_args( - operation: &str, - contextual_args: &Option<(HashSet, usize)>, -) -> Result, ContextBatchingError> { - if let Some((ctx, times)) = contextual_args { - let parser = apollo_compiler::Parser::new() - .parse_ast(operation, "") - .map_err(ContextBatchingError::InvalidDocument)?; - if let Some(mut operation) = parser - .definitions - .into_iter() - .find_map(|definition| definition.as_operation_definition().cloned()) - { - let mut new_variables: Vec<_> = Default::default(); - if operation - .variables - .iter() - .any(|v| ctx.contains(v.name.as_str())) - { - let new_selection_set: Vec<_> = (0..*times) - .map(|i| { - // TODO: Unwrap - let mut s = operation - .selection_set - .first() - .ok_or_else(|| ContextBatchingError::NoSelectionSet)? - .clone(); - if let ast::Selection::Field(f) = &mut s { - let f = f.make_mut(); - f.alias = Some(Name::new_unchecked(format!("_{}", i).into())); - } - - for v in &operation.variables { - if ctx.contains(v.name.as_str()) { - let mut cloned = v.clone(); - let new_variable = cloned.make_mut(); - new_variable.name = - Name::new_unchecked(format!("{}_{}", v.name, i).into()); - new_variables.push(Node::new(new_variable.clone())); - - s = rename_variables(s, v.name.clone(), new_variable.name.clone()); - } else if !new_variables.iter().any(|var| var.name == v.name) { - new_variables.push(v.clone()); - } - } - - Ok(s) - }) - .collect::, _>>()?; - - let new_operation = operation.make_mut(); - new_operation.selection_set = new_selection_set; - new_operation.variables = new_variables; - - return Ok(Some(new_operation.serialize().no_indent().to_string())); - } - } - } - - Ok(None) -} - -fn rename_variables(selection_set: ast::Selection, from: Name, to: Name) -> ast::Selection { - match selection_set { - ast::Selection::Field(f) => { - let mut new = f.clone(); - - let as_mut = new.make_mut(); - - as_mut.arguments.iter_mut().for_each(|arg| { - if arg.value.as_variable() == Some(&from) { - arg.make_mut().value = ast::Value::Variable(to.clone()).into(); - } - }); - - as_mut.selection_set = as_mut - .selection_set - .clone() - .into_iter() - .map(|s| rename_variables(s, from.clone(), to.clone())) - .collect(); - - ast::Selection::Field(new) - } - ast::Selection::InlineFragment(f) => { - let mut new = f.clone(); - new.make_mut().selection_set = f - .selection_set - .clone() - .into_iter() - .map(|s| rename_variables(s, from.clone(), to.clone())) - .collect(); - ast::Selection::InlineFragment(new) - } - ast::Selection::FragmentSpread(f) => ast::Selection::FragmentSpread(f), - } -} - -#[test] -fn test_query_batching_for_contextual_args() { - let old_query = "query QueryLL__Subgraph1__1($representations:[_Any!]!$Subgraph1_U_field_a:String!){_entities(representations:$representations){...on U{id field(a:$Subgraph1_U_field_a)}}}"; - let mut ctx_args = HashSet::new(); - ctx_args.insert("Subgraph1_U_field_a".to_string()); - let contextual_args = Some((ctx_args, 2)); - - let expected = "query QueryLL__Subgraph1__1($representations: [_Any!]!, $Subgraph1_U_field_a_0: String!, $Subgraph1_U_field_a_1: String!) { _0: _entities(representations: $representations) { ... on U { id field(a: $Subgraph1_U_field_a_0) } } _1: _entities(representations: $representations) { ... on U { id field(a: $Subgraph1_U_field_a_1) } } }"; - - assert_eq!( - expected, - query_batching_for_contextual_args(old_query, &contextual_args) - .unwrap() - .unwrap() - ); -} - -// TODO: There is probably a function somewhere else that already does this -fn data_at_path<'v>(data: &'v Value, path: &Path) -> Option<&'v Value> { - let v = match &path.0[0] { - PathElement::Fragment(s) => { - // get the value at data.get("__typename") and compare it with s. If the values are equal, return data, otherwise None - let mut z: Option<&Value> = None; - let wrapped_typename = data.get("__typename"); - if let Some(t) = wrapped_typename { - if t.as_str() == Some(s.as_str()) { - z = Some(data); - } - } - z - } - PathElement::Key(v, _) => data.get(v), - PathElement::Index(idx) => Some(&data[idx]), - PathElement::Flatten(_) => None, - }; - - if path.len() > 1 { - if let Some(val) = v { - let remaining_path = path.iter().skip(1).cloned().collect(); - return data_at_path(val, &remaining_path); - } - } - v -} - -fn merge_context_path(current_dir: &Path, context_path: &Path) -> Path { - let mut i = 0; - let mut j = current_dir.len(); - // iterate over the context_path(i), every time we encounter a '..', we want - // to go up one level in the current_dir(j) - while i < context_path.len() { - match &context_path.0.get(i) { - Some(PathElement::Key(e, _)) => { - let mut found = false; - if e == ".." { - while !found { - j -= 1; - if let Some(PathElement::Key(_, _)) = current_dir.0.get(j) { - found = true; - } - } - i += 1; - } else { - break; - } - } - _ => break, - } - } - - let mut return_path: Vec = current_dir.iter().take(j).cloned().collect(); - - context_path.iter().skip(i).for_each(|e| { - return_path.push(e.clone()); - }); - Path(return_path.into_iter().collect()) + pub(crate) contextual_arguments: Option, } impl Variables { @@ -453,79 +267,26 @@ impl Variables { input_rewrites: &Option>, context_rewrites: &Option>, ) -> Option { - let mut variables: serde_json_bytes::Map = - Object::with_capacity(1 + variable_usages.len()); - let body = request.body(); + let mut subgraph_context = SubgraphContext::new(data, current_dir, schema, context_rewrites); + dbg!("new variables"); + if !requires.is_empty() { + let mut variables = Object::with_capacity(1 + variable_usages.len()); - variables.extend(variable_usages.iter().filter_map(|key| { - body.variables - .get_key_value(key.as_str()) - .map(|(variable_key, value)| (variable_key.clone(), value.clone())) - })); + variables.extend(variable_usages.iter().filter_map(|key| { + body.variables + .get_key_value(key.as_str()) + .map(|(variable_key, value)| (variable_key.clone(), value.clone())) + })); - if !requires.is_empty() { let mut inverted_paths: Vec> = Vec::new(); let mut values: IndexSet = IndexSet::new(); - let mut named_args: Vec> = Vec::new(); data.select_values_and_paths(schema, current_dir, |path, value| { + dbg!(&path, &value); // first get contextual values that are required - let mut found_rewrites: HashSet = HashSet::new(); - let hash_map: HashMap = context_rewrites - .iter() - .flatten() - .filter_map(|rewrite| { - // note that it's possible that we could have multiple rewrites for the same variable. If that's the case, don't lookup if a value has already been found - match rewrite { - DataRewrite::KeyRenamer(item) => { - if !found_rewrites.contains(item.rename_key_to.as_str()) { - let data_path = merge_context_path(path, &item.path); - let val = data_at_path(data, &data_path); - if let Some(v) = val { - // add to found - found_rewrites - .insert(item.rename_key_to.clone().to_string()); - // TODO: not great - let mut new_value = v.clone(); - if let Some(values) = new_value.as_array_mut() { - for v in values { - rewrites::apply_single_rewrite( - schema, - v, - &DataRewrite::KeyRenamer({ - DataKeyRenamer { - path: data_path.clone(), - rename_key_to: item - .rename_key_to - .clone(), - } - }), - ); - } - } else { - rewrites::apply_single_rewrite( - schema, - &mut new_value, - &DataRewrite::KeyRenamer({ - DataKeyRenamer { - path: data_path, - rename_key_to: item.rename_key_to.clone(), - } - }), - ); - } - return Some((item.rename_key_to.to_string(), new_value)); - } - } - None - } - DataRewrite::ValueSetter(_) => { - // TODO: Log error? panic? not sure - None - } - } - }) - .collect(); + if let Some(context) = subgraph_context.as_mut() { + context.execute_on_path(path); + } let mut value = execute_selection_set(value, requires, schema, None); if value.as_object().map(|o| !o.is_empty()).unwrap_or(false) { @@ -533,13 +294,11 @@ impl Variables { match values.get_index_of(&value) { Some(index) => { inverted_paths[index].push(path.clone()); - named_args.push(hash_map); } None => { inverted_paths.push(vec![path.clone()]); values.insert(value); debug_assert!(inverted_paths.len() == values.len()); - named_args.push(hash_map); } } } @@ -550,47 +309,16 @@ impl Variables { } let representations = Value::Array(Vec::from_iter(values)); - - // Here we create a new map with all the key value pairs to push into variables. - // Note that if all variables are the same, we just use the named parameter as a variable, but if they are different then each - // entity will have it's own set of parameters all appended by _ - let (extended_vars, contextual_args) = if let Some(first_map) = named_args.first() { - if named_args.iter().all(|map| map == first_map) { - ( - first_map - .iter() - .map(|(k, v)| (k.as_str().into(), v.clone())) - .collect(), - None, - ) - } else { - let mut hash_map: HashMap = HashMap::new(); - let arg_names: HashSet<_> = first_map.keys().cloned().collect(); - for (index, item) in named_args.iter().enumerate() { - // append _ to each of the arguments and push all the values into hash_map - hash_map.extend(item.iter().map(|(k, v)| { - let mut new_named_param = k.clone(); - new_named_param.push_str(&format!("_{}", index)); - (new_named_param, v.clone()) - })); - } - (hash_map, Some((arg_names, named_args.len()))) - } - } else { - (HashMap::new(), None) + let contextual_arguments = match subgraph_context.as_mut() { + Some(context) => context.add_variables_and_get_args(&mut variables), + None => None, }; - - variables.extend( - extended_vars - .iter() - .map(|(key, value)| (key.as_str().into(), value.clone())), - ); - + variables.insert("representations", representations); Some(Variables { variables, inverted_paths, - contextual_args, + contextual_arguments, }) } else { // with nested operations (Query or Mutation has an operation returning a Query or Mutation), @@ -611,13 +339,13 @@ impl Variables { variables: variable_usages .iter() .filter_map(|key| { - variables + body.variables .get_key_value(key.as_str()) .map(|(variable_key, value)| (variable_key.clone(), value.clone())) }) .collect::(), inverted_paths: Vec::new(), - contextual_args: None, + contextual_arguments: None, }) } } @@ -650,7 +378,7 @@ impl FetchNode { let Variables { variables, inverted_paths: paths, - contextual_args, + contextual_arguments, } = match Variables::new( &self.requires, &self.variable_usages, @@ -667,16 +395,11 @@ impl FetchNode { return (Value::Object(Object::default()), Vec::new()); } }; - - let query_batched_query = if let Ok(qbq) = - query_batching_for_contextual_args(operation.as_serialized(), &contextual_args) - { - qbq - } else { - // TODO: do we need to provide an error here ? - return (Default::default(), Default::default()); - }; - + + let aliased_operation = build_operation_with_aliasing(operation, &contextual_arguments, parameters.schema); + if let Ok(a) = aliased_operation { + dbg!(a.to_string()); + } let mut subgraph_request = SubgraphRequest::builder() .supergraph_request(parameters.supergraph_request.clone()) .subgraph_request( @@ -695,7 +418,7 @@ impl FetchNode { ) .body( Request::builder() - .query(query_batched_query.as_deref().unwrap_or(operation.as_serialized())) + .query(operation.as_serialized()) .and_operation_name(operation_name.as_ref().map(|n| n.to_string())) .variables(variables.clone()) .build(), @@ -763,6 +486,7 @@ impl FetchNode { .to_graphql_error(Some(current_dir.to_owned()))], ); } + let (value, errors) = self.response_at_path(parameters.schema, current_dir, paths, response); if let Some(id) = &self.id { diff --git a/apollo-router/src/query_planner/mod.rs b/apollo-router/src/query_planner/mod.rs index a11a5cb072..3b3ea97f67 100644 --- a/apollo-router/src/query_planner/mod.rs +++ b/apollo-router/src/query_planner/mod.rs @@ -20,6 +20,7 @@ mod labeler; mod plan; pub(crate) mod rewrites; mod selection; +pub(crate) mod subgraph_context; pub(crate) mod subscription; pub(crate) const FETCH_SPAN_NAME: &str = "fetch"; diff --git a/apollo-router/src/query_planner/subgraph_context.rs b/apollo-router/src/query_planner/subgraph_context.rs new file mode 100644 index 0000000000..13aa5c4783 --- /dev/null +++ b/apollo-router/src/query_planner/subgraph_context.rs @@ -0,0 +1,503 @@ +use std::collections::HashMap; +use std::collections::HashSet; +use apollo_compiler::ast; +use apollo_compiler::ast::Document; +use apollo_compiler::ast::Name; +use apollo_compiler::ast::VariableDefinition; +use apollo_compiler::executable; +use apollo_compiler::executable::Operation; +use apollo_compiler::executable::Selection; +use apollo_compiler::executable::SelectionSet; +use apollo_compiler::validation::WithErrors; +use apollo_compiler::Node; +use apollo_compiler::NodeStr; +use serde_json_bytes::ByteString; +use serde_json_bytes::Map; + +use super::fetch::SubgraphOperation; +use super::rewrites; +use super::rewrites::DataKeyRenamer; +use super::rewrites::DataRewrite; +// use crate::json_ext::Object; + +use crate::json_ext::Path; +use crate::json_ext::PathElement; +use crate::json_ext::Value; +// use crate::json_ext::ValueExt; +use crate::spec::Schema; + +pub(crate) struct ContextualArguments { + pub(crate) arguments: HashSet, // a set of all argument names that will be passed to the subgraph. This is the unmodified name from the query plan + pub(crate) count: usize, // the number of different sets of arguments that exist. This will either be 1 or the number of entities +} + +pub(crate) struct SubgraphContext<'a> { + pub(crate) data: &'a Value, + pub(crate) current_dir: &'a Path, + pub(crate) schema: &'a Schema, + pub(crate) context_rewrites: &'a Vec, + pub(crate) named_args: Vec>, +} + +// TODO: There is probably a function somewhere else that already does this +fn data_at_path<'v>(data: &'v Value, path: &Path) -> Option<&'v Value> { + let v = match &path.0[0] { + PathElement::Fragment(s) => { + // get the value at data.get("__typename") and compare it with s. If the values are equal, return data, otherwise None + let mut z: Option<&Value> = None; + let wrapped_typename = data.get("__typename"); + if let Some(t) = wrapped_typename { + if t.as_str() == Some(s.as_str()) { + z = Some(data); + } + } + z + } + PathElement::Key(v, _) => data.get(v), + PathElement::Index(idx) => Some(&data[idx]), + PathElement::Flatten(_) => None, + }; + + if path.len() > 1 { + if let Some(val) = v { + let remaining_path = path.iter().skip(1).cloned().collect(); + return data_at_path(val, &remaining_path); + } + } + v +} + +fn merge_context_path(current_dir: &Path, context_path: &Path) -> Path { + let mut i = 0; + let mut j = current_dir.len(); + // iterate over the context_path(i), every time we encounter a '..', we want + // to go up one level in the current_dir(j) + while i < context_path.len() { + match &context_path.0.get(i) { + Some(PathElement::Key(e, _)) => { + let mut found = false; + if e == ".." { + while !found { + j -= 1; + if let Some(PathElement::Key(_, _)) = current_dir.0.get(j) { + found = true; + } + } + i += 1; + } else { + break; + } + } + _ => break, + } + } + + let mut return_path: Vec = current_dir.iter().take(j).cloned().collect(); + + context_path.iter().skip(i).for_each(|e| { + return_path.push(e.clone()); + }); + Path(return_path.into_iter().collect()) +} + + +impl<'a> SubgraphContext<'a> { + pub(crate) fn new( + data: &'a Value, + current_dir: &'a Path, + schema: &'a Schema, + context_rewrites: &'a Option>, + ) -> Option> { + if let Some(rewrites) = context_rewrites { + if rewrites.len() > 0 { + return Some(SubgraphContext { + data, + current_dir, + schema, + context_rewrites: rewrites, + named_args: Vec::new(), + }) + } + } + None + } + + pub(crate) fn execute_on_path( + &mut self, + path: &Path, + ) { + let mut found_rewrites: HashSet = HashSet::new(); + let hash_map: HashMap = self.context_rewrites + .iter() + .filter_map(|rewrite| { + match rewrite { + DataRewrite::KeyRenamer(item) => { + if !found_rewrites.contains(item.rename_key_to.as_str()) { + let data_path = merge_context_path(path, &item.path); + let val = data_at_path(self.data, &data_path); + if let Some(v) = val { + // add to found + found_rewrites + .insert(item.rename_key_to.clone().to_string()); + // TODO: not great + let mut new_value = v.clone(); + if let Some(values) = new_value.as_array_mut() { + for v in values { + rewrites::apply_single_rewrite( + self.schema, + v, + &DataRewrite::KeyRenamer({ + DataKeyRenamer { + path: data_path.clone(), + rename_key_to: item + .rename_key_to + .clone(), + } + }), + ); + } + } else { + rewrites::apply_single_rewrite( + self.schema, + &mut new_value, + &DataRewrite::KeyRenamer({ + DataKeyRenamer { + path: data_path, + rename_key_to: item.rename_key_to.clone(), + } + }), + ); + } + return Some((item.rename_key_to.to_string(), new_value)); + } + } + None + }, + DataRewrite::ValueSetter(_) => { + None + } + } + }) + .collect(); + self.named_args.push(hash_map); + } + + pub(crate) fn add_variables_and_get_args( + &self, + variables: &mut Map, + ) -> Option { + dbg!(&self.named_args); + let (extended_vars, contextual_args) = if let Some(first_map) = self.named_args.first() { + if self.named_args.iter().all(|map| map == first_map) { + ( + first_map + .iter() + .map(|(k, v)| (k.as_str().into(), v.clone())) + .collect(), + None, + ) + } else { + let mut hash_map: HashMap = HashMap::new(); + let arg_names: HashSet<_> = first_map.keys().cloned().collect(); + for (index, item) in self.named_args.iter().enumerate() { + // append _ to each of the arguments and push all the values into hash_map + hash_map.extend(item.iter().map(|(k, v)| { + let mut new_named_param = k.clone(); + new_named_param.push_str(&format!("_{}", index)); + (new_named_param, v.clone()) + })); + } + (hash_map, Some(ContextualArguments{ + arguments: arg_names, + count: self.named_args.len(), + })) + } + } else { + (HashMap::new(), None) + }; + + variables.extend( + extended_vars + .iter() + .map(|(key, value)| (key.as_str().into(), value.clone())), + ); + + contextual_args + } +} + +// fn add_index_alias_to_first_selection( +// selection_set: &SelectionSet, +// index: usize, +// ) -> Result { +// let selection = selection_set.selections.first(); +// if let Some(executable::Selection::Field(node)) = selection { +// let z: Selection = Node::new(executable::Field { +// definition: node.definition.clone(), +// alias: node.alias.clone(), +// name: node.name.clone(), +// arguments: node.arguments.clone(), +// directives: node.directives.clone(), +// selection_set: node.selection_set.clone(), +// }); +// Ok(z) +// } else { +// Err(ContextBatchingError::NoSelectionSet) +// } +// } + +pub fn build_operation_with_aliasing( + subgraph_operation: &SubgraphOperation, + contextual_arguments: &Option, + schema: &Schema, +) -> Result { + let mut selections: Vec = vec![]; + dbg!(subgraph_operation.as_serialized()); + match contextual_arguments { + Some(ContextualArguments { arguments, count }) => { + dbg!("arguments", arguments); + let parsed_document = subgraph_operation.as_parsed(schema.supergraph_schema()); + if let Ok(document) = parsed_document { + // TODO: Can there be more than one named operation? + // Can there be an anonymous operation? + // let mut new_variables: Vec> = vec![]; + // let mut new_selection_set: Vec = vec![]; + // let mut operations: Vec = vec![]; + if let Some((_, op)) = document.named_operations.first() { + let mut new_variables: Vec> = vec![]; + op.variables.iter().for_each(|v| { + if arguments.contains(v.name.as_str()) { + for i in 0..*count { + new_variables.push( + Node::new(VariableDefinition { + name: Name::new_unchecked(format!("{}_{}", v.name.as_str(), i).into()), + ty: v.ty.clone(), + default_value: v.default_value.clone(), + directives: v.directives.clone(), + }) + ); + } + } else { + new_variables.push(v.clone()); + } + }); + + for i in 0..*count { + // If we are aliasing, we know that there is only one selection in the top level SelectionSet + // it is a field selection for _entities, so it's ok to reach in and give it an alias + let selection_set = transform_selection_set(&op.selection_set, arguments, i, true); + selections.push(selection_set.selections[0].clone()) + }; + + Ok( + Operation { + operation_type: op.operation_type.clone(), + name: op.name.clone(), + directives: op.directives.clone(), + variables: new_variables, + selection_set: SelectionSet { + ty: op.selection_set.ty.clone(), + selections, + }, + } + ) + } else { + Err(ContextBatchingError::NoSelectionSet) + } + } else { + Err(ContextBatchingError::NoSelectionSet) + } + }, + None => Err(ContextBatchingError::NoSelectionSet), + } +} + +fn transform_field_arguments( + arguments_in_selection: &Vec>, + arguments: &HashSet, + index: usize, +) -> Vec> { + arguments_in_selection.iter().map(|arg| { + Node::new( + ast::Argument { + name: if arguments.contains(arg.name.as_str()) { + Name::new_unchecked(format!("{}_{}", arg.value.clone(), index).into()) + } else { + arg.name.clone() + }, + value: arg.value.clone(), + } + ) + }).collect() +} + +fn transform_selection_set( + selection_set: &SelectionSet, + arguments: &HashSet, + index: usize, + add_alias: bool, +) -> SelectionSet { + SelectionSet{ + ty: selection_set.ty.clone(), + selections: selection_set.selections.iter().map(|selection| { + match selection { + executable::Selection::Field(node) => { + executable::Selection::Field( + Node::new(executable::Field { + definition: node.definition.clone(), + alias: if add_alias { + Some(Name::new_unchecked(format!("_{}", index).into())) + } else { + node.alias.clone() + }, + name: node.name.clone(), + arguments: transform_field_arguments(&node.arguments, arguments, index), + directives: node.directives.clone(), + selection_set: transform_selection_set(&node.selection_set, arguments, index, false), + }) + ) + }, + executable::Selection::FragmentSpread(node) => { + executable::Selection::FragmentSpread( + Node::new(executable::FragmentSpread { + fragment_name: node.fragment_name.clone(), + directives: node.directives.clone(), + }) + ) + }, + executable::Selection::InlineFragment(node) => { + executable::Selection::InlineFragment( + Node::new(executable::InlineFragment { + type_condition: node.type_condition.clone(), + directives: node.directives.clone(), + selection_set: transform_selection_set(&node.selection_set, arguments, index, false), + }) + ) + }, + } + }).collect(), + } +} + +// // to delete +// fn query_batching_for_contextual_args( +// operation: &str, +// contextual_arguments: &Option, +// ) -> Result, ContextBatchingError> { +// if let Some(ContextualArguments { arguments, count }) = contextual_arguments { +// let parser = apollo_compiler::Parser::new() +// .parse_ast(operation, "") +// .map_err(ContextBatchingError::InvalidDocument)?; +// if let Some(mut operation) = parser +// .definitions +// .into_iter() +// .find_map(|definition| definition.as_operation_definition().cloned()) +// { +// let mut new_variables = vec![]; +// if operation +// .variables +// .iter() +// .any(|v| arguments.contains(v.name.as_str())) +// { +// let new_selection_set: Vec<_> = (0..*count) +// .map(|i| { +// // TODO: Unwrap +// let mut s = operation +// .selection_set +// .first() +// .ok_or_else(|| ContextBatchingError::NoSelectionSet)? +// .clone(); +// if let ast::Selection::Field(f) = &mut s { +// let f = f.make_mut(); +// f.alias = Some(Name::new_unchecked(format!("_{}", i).into())); +// } + +// for v in &operation.variables { +// if arguments.contains(v.name.as_str()) { +// let mut cloned = v.clone(); +// let new_variable = cloned.make_mut(); +// new_variable.name = +// Name::new_unchecked(format!("{}_{}", v.name, i).into()); +// new_variables.push(Node::new(new_variable.clone())); + +// s = rename_variables(s, v.name.clone(), new_variable.name.clone()); +// } else if !new_variables.iter().any(|var| var.name == v.name) { +// new_variables.push(v.clone()); +// } +// } + +// Ok(s) +// }) +// .collect::, _>>()?; + +// let new_operation = operation.make_mut(); +// new_operation.selection_set = new_selection_set; +// new_operation.variables = new_variables; + +// return Ok(Some(new_operation.serialize().no_indent().to_string())); +// } +// } +// } + +// Ok(None) +// } + +// fn rename_variables(selection_set: ast::Selection, from: Name, to: Name) -> ast::Selection { +// match selection_set { +// ast::Selection::Field(f) => { +// let mut new = f.clone(); + +// let as_mut = new.make_mut(); + +// as_mut.arguments.iter_mut().for_each(|arg| { +// if arg.value.as_variable() == Some(&from) { +// arg.make_mut().value = ast::Value::Variable(to.clone()).into(); +// } +// }); + +// as_mut.selection_set = as_mut +// .selection_set +// .clone() +// .into_iter() +// .map(|s| rename_variables(s, from.clone(), to.clone())) +// .collect(); + +// ast::Selection::Field(new) +// } +// ast::Selection::InlineFragment(f) => { +// let mut new = f.clone(); +// new.make_mut().selection_set = f +// .selection_set +// .clone() +// .into_iter() +// .map(|s| rename_variables(s, from.clone(), to.clone())) +// .collect(); +// ast::Selection::InlineFragment(new) +// } +// ast::Selection::FragmentSpread(f) => ast::Selection::FragmentSpread(f), +// } +// } + +#[derive(Debug)] +pub enum ContextBatchingError { + InvalidDocument(WithErrors), + NoSelectionSet, +} + +// #[derive(Debug)] +// #[test] +// fn test_query_batching_for_contextual_args() { +// let old_query = "query QueryLL__Subgraph1__1($representations:[_Any!]!$Subgraph1_U_field_a:String!){_entities(representations:$representations){...on U{id field(a:$Subgraph1_U_field_a)}}}"; +// let mut ctx_args = HashSet::new(); +// ctx_args.insert("Subgraph1_U_field_a".to_string()); +// let contextual_args = Some((ctx_args, 2)); + +// let expected = "query QueryLL__Subgraph1__1($representations: [_Any!]!, $Subgraph1_U_field_a_0: String!, $Subgraph1_U_field_a_1: String!) { _0: _entities(representations: $representations) { ... on U { id field(a: $Subgraph1_U_field_a_0) } } _1: _entities(representations: $representations) { ... on U { id field(a: $Subgraph1_U_field_a_1) } } }"; + +// assert_eq!( +// expected, +// query_batching_for_contextual_args(old_query, &contextual_args) +// .unwrap() +// .unwrap() +// ); +// } diff --git a/apollo-router/tests/fixtures/set_context/one.json b/apollo-router/tests/fixtures/set_context/one.json index e03720c4d5..e9bc0e49f3 100644 --- a/apollo-router/tests/fixtures/set_context/one.json +++ b/apollo-router/tests/fixtures/set_context/one.json @@ -74,6 +74,7 @@ "data": { "tList": [ { + "__typename": "T", "prop": "prop value 1", "id": "1", "uList": [ @@ -84,6 +85,7 @@ ] }, { + "__typename": "T", "prop": "prop value 2", "id": "2", "uList": [ @@ -118,7 +120,7 @@ }, { "request": { - "query": "query Query__Subgraph1__1($representations:[_Any!]!$contextualArgument_1_0:String!){_entities(representations:$representations){...on U{__typename field(a:$contextualArgument_1_0)}}}", + "query": "query Query__Subgraph1__1($representations:[_Any!]!$contextualArgument_1_0:String){_entities(representations:$representations){...on U{field(a:$contextualArgument_1_0)}}}", "operationName": "Query__Subgraph1__1", "variables": { "contextualArgument_1_0": "prop value", @@ -129,7 +131,6 @@ "data": { "_entities": [ { - "__typename": "U", "id": "1", "field": 1234 } diff --git a/apollo-router/tests/fixtures/set_context/supergraph.graphql b/apollo-router/tests/fixtures/set_context/supergraph.graphql index 1e2c909ed9..87fe61a3d3 100644 --- a/apollo-router/tests/fixtures/set_context/supergraph.graphql +++ b/apollo-router/tests/fixtures/set_context/supergraph.graphql @@ -94,16 +94,8 @@ type Query @join__type(graph: SUBGRAPH1) @join__type(graph: SUBGRAPH2) { t: T! @join__field(graph: SUBGRAPH1) tList: [T]! @join__field(graph: SUBGRAPH1) a: Int! @join__field(graph: SUBGRAPH2) - k: K! @join__field(graph: SUBGRAPH1) } -union K - @join__type(graph: SUBGRAPH1) - @join__unionMember(graph: SUBGRAPH1, member: "A") - @join__unionMember(graph: SUBGRAPH1, member: "B") - @context(name: "Subgraph1__context") - = A | B - type A @join__type(graph: SUBGRAPH1, key: "id") { @@ -141,7 +133,7 @@ type U { context: "Subgraph1__context" name: "a" - type: "String!" + type: "String" selection: "{ prop }" } ] @@ -160,8 +152,8 @@ type V { context: "Subgraph1__context" name: "a" - type: "String!" - selection: "... on A { prop } ... on B { prop } ... on T { prop }" + type: "String" + selection: "... on T { prop }" } ] ) diff --git a/apollo-router/tests/snapshots/set_context__set_context.snap.new b/apollo-router/tests/snapshots/set_context__set_context.snap.new new file mode 100644 index 0000000000..e1c7b6d5ee --- /dev/null +++ b/apollo-router/tests/snapshots/set_context__set_context.snap.new @@ -0,0 +1,102 @@ +--- +source: apollo-router/tests/set_context.rs +assertion_line: 60 +expression: response +--- +{ + "data": { + "t": { + "__typename": "T", + "id": "1", + "u": { + "__typename": "U", + "field": 1234 + } + } + }, + "extensions": { + "apolloQueryPlan": { + "object": { + "kind": "QueryPlan", + "node": { + "kind": "Sequence", + "nodes": [ + { + "kind": "Fetch", + "serviceName": "Subgraph1", + "variableUsages": [], + "operation": "query Query__Subgraph1__0{t{__typename prop id u{__typename id}}}", + "operationName": "Query__Subgraph1__0", + "operationKind": "query", + "id": null, + "inputRewrites": null, + "outputRewrites": null, + "contextRewrites": null, + "schemaAwareHash": "d7cb2d1809789d49360ca0a60570555f83855f00547675f366915c9d9d90fef9", + "authorization": { + "is_authenticated": false, + "scopes": [], + "policies": [] + } + }, + { + "kind": "Flatten", + "path": [ + "", + "t", + "u" + ], + "node": { + "kind": "Fetch", + "serviceName": "Subgraph1", + "requires": [ + { + "kind": "InlineFragment", + "typeCondition": "U", + "selections": [ + { + "kind": "Field", + "name": "__typename" + }, + { + "kind": "Field", + "name": "id" + } + ] + } + ], + "variableUsages": [ + "contextualArgument_1_0" + ], + "operation": "query Query__Subgraph1__1($representations:[_Any!]!$contextualArgument_1_0:String){_entities(representations:$representations){...on U{field(a:$contextualArgument_1_0)}}}", + "operationName": "Query__Subgraph1__1", + "operationKind": "query", + "id": null, + "inputRewrites": null, + "outputRewrites": null, + "contextRewrites": [ + { + "kind": "KeyRenamer", + "path": [ + "..", + "... on T", + "prop" + ], + "renameKeyTo": "contextualArgument_1_0" + } + ], + "schemaAwareHash": "66b954f39aead8436321c671eb71e56ce15bbe0c7b82f06b2f8f70473ce1cb6e", + "authorization": { + "is_authenticated": false, + "scopes": [], + "policies": [] + } + } + } + ] + } + }, + "text": "QueryPlan {\n Sequence {\n Fetch(service: \"Subgraph1\") {\n {\n t {\n __typename\n prop\n id\n u {\n __typename\n id\n }\n }\n }\n },\n Flatten(path: \".t.u\") {\n Fetch(service: \"Subgraph1\") {\n {\n ... on U {\n __typename\n id\n }\n } =>\n {\n ... on U {\n field(a: $contextualArgument_1_0)\n }\n }\n },\n },\n },\n}" + } + } +} diff --git a/apollo-router/tests/snapshots/set_context__set_context_list_of_lists.snap.new b/apollo-router/tests/snapshots/set_context__set_context_list_of_lists.snap.new new file mode 100644 index 0000000000..120b6dc95a --- /dev/null +++ b/apollo-router/tests/snapshots/set_context__set_context_list_of_lists.snap.new @@ -0,0 +1,138 @@ +--- +source: apollo-router/tests/set_context.rs +assertion_line: 171 +expression: response +--- +{ + "data": { + "tList": [ + { + "id": "1", + "uList": [ + null + ] + }, + { + "id": "2", + "uList": [ + null + ] + } + ] + }, + "errors": [ + { + "message": "couldn't find mock for query {\"query\":\"query QueryLL__Subgraph1__1($representations:[_Any!]!$contextualArgument_1_0:String){_entities(representations:$representations){...on U{field(a:$contextualArgument_1_0)}}}\",\"operationName\":\"QueryLL__Subgraph1__1\",\"variables\":{\"contextualArgument_1_0_0\":\"prop value 1\",\"contextualArgument_1_0_1\":\"prop value 2\",\"representations\":[{\"__typename\":\"U\",\"id\":\"3\"},{\"__typename\":\"U\",\"id\":\"4\"}]}}", + "extensions": { + "code": "FETCH_ERROR" + } + } + ], + "extensions": { + "valueCompletion": [ + { + "message": "Cannot return null for non-nullable field U.field", + "path": [ + "tList", + 0, + "uList", + 0 + ] + }, + { + "message": "Cannot return null for non-nullable field U.field", + "path": [ + "tList", + 1, + "uList", + 0 + ] + } + ], + "apolloQueryPlan": { + "object": { + "kind": "QueryPlan", + "node": { + "kind": "Sequence", + "nodes": [ + { + "kind": "Fetch", + "serviceName": "Subgraph1", + "variableUsages": [], + "operation": "query QueryLL__Subgraph1__0{tList{prop id uList{__typename id}}}", + "operationName": "QueryLL__Subgraph1__0", + "operationKind": "query", + "id": null, + "inputRewrites": null, + "outputRewrites": null, + "contextRewrites": null, + "schemaAwareHash": "20170491dab082e15802bd0b9e6abdf2c6803589cc969668e8b0bc8388af0542", + "authorization": { + "is_authenticated": false, + "scopes": [], + "policies": [] + } + }, + { + "kind": "Flatten", + "path": [ + "", + "tList", + "@", + "uList", + "@" + ], + "node": { + "kind": "Fetch", + "serviceName": "Subgraph1", + "requires": [ + { + "kind": "InlineFragment", + "typeCondition": "U", + "selections": [ + { + "kind": "Field", + "name": "__typename" + }, + { + "kind": "Field", + "name": "id" + } + ] + } + ], + "variableUsages": [ + "contextualArgument_1_0" + ], + "operation": "query QueryLL__Subgraph1__1($representations:[_Any!]!$contextualArgument_1_0:String){_entities(representations:$representations){...on U{field(a:$contextualArgument_1_0)}}}", + "operationName": "QueryLL__Subgraph1__1", + "operationKind": "query", + "id": null, + "inputRewrites": null, + "outputRewrites": null, + "contextRewrites": [ + { + "kind": "KeyRenamer", + "path": [ + "..", + "... on T", + "prop" + ], + "renameKeyTo": "contextualArgument_1_0" + } + ], + "schemaAwareHash": "a9b24549250c12e38c398c32e9218134fab000be3b934ebc6bb38ea096343646", + "authorization": { + "is_authenticated": false, + "scopes": [], + "policies": [] + } + } + } + ] + } + }, + "text": "QueryPlan {\n Sequence {\n Fetch(service: \"Subgraph1\") {\n {\n tList {\n prop\n id\n uList {\n __typename\n id\n }\n }\n }\n },\n Flatten(path: \".tList.@.uList.@\") {\n Fetch(service: \"Subgraph1\") {\n {\n ... on U {\n __typename\n id\n }\n } =>\n {\n ... on U {\n field(a: $contextualArgument_1_0)\n }\n }\n },\n },\n },\n}" + } + } +} From 44154e5b910dba74c78725ba30d169f75453dea2 Mon Sep 17 00:00:00 2001 From: Chris Lenfest Date: Wed, 15 May 2024 18:35:52 -0500 Subject: [PATCH 30/69] another_checkpoint --- apollo-router/src/query_planner/fetch.rs | 2 +- .../src/query_planner/subgraph_context.rs | 109 ++++++++---------- ...ontext__set_context_list_of_lists.snap.new | 2 +- .../set_context__set_context_union.snap.new | 21 ++++ 4 files changed, 69 insertions(+), 65 deletions(-) create mode 100644 apollo-router/tests/snapshots/set_context__set_context_union.snap.new diff --git a/apollo-router/src/query_planner/fetch.rs b/apollo-router/src/query_planner/fetch.rs index a06cad2d23..16a8eadf01 100644 --- a/apollo-router/src/query_planner/fetch.rs +++ b/apollo-router/src/query_planner/fetch.rs @@ -398,7 +398,7 @@ impl FetchNode { let aliased_operation = build_operation_with_aliasing(operation, &contextual_arguments, parameters.schema); if let Ok(a) = aliased_operation { - dbg!(a.to_string()); + dbg!(a.serialize().no_indent()); } let mut subgraph_request = SubgraphRequest::builder() .supergraph_request(parameters.supergraph_request.clone()) diff --git a/apollo-router/src/query_planner/subgraph_context.rs b/apollo-router/src/query_planner/subgraph_context.rs index 13aa5c4783..ea67c2f64f 100644 --- a/apollo-router/src/query_planner/subgraph_context.rs +++ b/apollo-router/src/query_planner/subgraph_context.rs @@ -9,8 +9,9 @@ use apollo_compiler::executable::Operation; use apollo_compiler::executable::Selection; use apollo_compiler::executable::SelectionSet; use apollo_compiler::validation::WithErrors; +use apollo_compiler::ExecutableDocument; use apollo_compiler::Node; -use apollo_compiler::NodeStr; +use indexmap::IndexMap; use serde_json_bytes::ByteString; use serde_json_bytes::Map; @@ -252,7 +253,6 @@ pub fn build_operation_with_aliasing( schema: &Schema, ) -> Result { let mut selections: Vec = vec![]; - dbg!(subgraph_operation.as_serialized()); match contextual_arguments { Some(ContextualArguments { arguments, count }) => { dbg!("arguments", arguments); @@ -263,7 +263,7 @@ pub fn build_operation_with_aliasing( // let mut new_variables: Vec> = vec![]; // let mut new_selection_set: Vec = vec![]; // let mut operations: Vec = vec![]; - if let Some((_, op)) = document.named_operations.first() { + if let Some((name, op)) = document.named_operations.first() { let mut new_variables: Vec> = vec![]; op.variables.iter().for_each(|v| { if arguments.contains(v.name.as_str()) { @@ -285,10 +285,11 @@ pub fn build_operation_with_aliasing( for i in 0..*count { // If we are aliasing, we know that there is only one selection in the top level SelectionSet // it is a field selection for _entities, so it's ok to reach in and give it an alias - let selection_set = transform_selection_set(&op.selection_set, arguments, i, true); + let mut selection_set = op.selection_set.clone(); + transform_selection_set(&mut selection_set, arguments, i, true); selections.push(selection_set.selections[0].clone()) }; - + Ok( Operation { operation_type: op.operation_type.clone(), @@ -312,73 +313,55 @@ pub fn build_operation_with_aliasing( } } -fn transform_field_arguments( - arguments_in_selection: &Vec>, - arguments: &HashSet, +fn add_alias_to_selection( + selection: &mut executable::Field, index: usize, -) -> Vec> { - arguments_in_selection.iter().map(|arg| { - Node::new( - ast::Argument { - name: if arguments.contains(arg.name.as_str()) { - Name::new_unchecked(format!("{}_{}", arg.value.clone(), index).into()) - } else { - arg.name.clone() - }, - value: arg.value.clone(), - } - ) - }).collect() +) { + selection.alias = Some(Name::new_unchecked(format!("_{}", index).into())); } fn transform_selection_set( - selection_set: &SelectionSet, + selection_set: &mut SelectionSet, arguments: &HashSet, index: usize, - add_alias: bool, -) -> SelectionSet { - SelectionSet{ - ty: selection_set.ty.clone(), - selections: selection_set.selections.iter().map(|selection| { - match selection { - executable::Selection::Field(node) => { - executable::Selection::Field( - Node::new(executable::Field { - definition: node.definition.clone(), - alias: if add_alias { - Some(Name::new_unchecked(format!("_{}", index).into())) - } else { - node.alias.clone() - }, - name: node.name.clone(), - arguments: transform_field_arguments(&node.arguments, arguments, index), - directives: node.directives.clone(), - selection_set: transform_selection_set(&node.selection_set, arguments, index, false), - }) - ) - }, - executable::Selection::FragmentSpread(node) => { - executable::Selection::FragmentSpread( - Node::new(executable::FragmentSpread { - fragment_name: node.fragment_name.clone(), - directives: node.directives.clone(), - }) - ) - }, - executable::Selection::InlineFragment(node) => { - executable::Selection::InlineFragment( - Node::new(executable::InlineFragment { - type_condition: node.type_condition.clone(), - directives: node.directives.clone(), - selection_set: transform_selection_set(&node.selection_set, arguments, index, false), - }) - ) - }, + add_alias: bool, // at the top level, we'll add an alias to field selections +) { + selection_set.selections.iter_mut().for_each(|selection| { + match selection { + executable::Selection::Field(node) => { + let node = node.make_mut(); + transform_field_arguments(&mut node.arguments, arguments, index); + transform_selection_set(&mut node.selection_set, arguments, index, false); + if add_alias { + add_alias_to_selection(node, index); + } + }, + executable::Selection::InlineFragment(node) => { + let node = node.make_mut(); + transform_selection_set(&mut node.selection_set, arguments, index, false); + }, + _ => (), + } + }); +} + +fn transform_field_arguments( + arguments_in_selection: &mut Vec>, + arguments: &HashSet, + index: usize, +) { + dbg!("scanning arguments"); + arguments_in_selection.iter_mut().for_each(|arg| { + let arg = arg.make_mut(); + if let Some(v) = arg.value.as_variable() { + if arguments.contains(v.as_str()) { + arg.value = Node::new(ast::Value::Variable(Name::new_unchecked(format!("{}_{}", v.as_str(), index).into()))); } - }).collect(), - } + } + }); } + // // to delete // fn query_batching_for_contextual_args( // operation: &str, diff --git a/apollo-router/tests/snapshots/set_context__set_context_list_of_lists.snap.new b/apollo-router/tests/snapshots/set_context__set_context_list_of_lists.snap.new index 120b6dc95a..dbf01c629a 100644 --- a/apollo-router/tests/snapshots/set_context__set_context_list_of_lists.snap.new +++ b/apollo-router/tests/snapshots/set_context__set_context_list_of_lists.snap.new @@ -22,7 +22,7 @@ expression: response }, "errors": [ { - "message": "couldn't find mock for query {\"query\":\"query QueryLL__Subgraph1__1($representations:[_Any!]!$contextualArgument_1_0:String){_entities(representations:$representations){...on U{field(a:$contextualArgument_1_0)}}}\",\"operationName\":\"QueryLL__Subgraph1__1\",\"variables\":{\"contextualArgument_1_0_0\":\"prop value 1\",\"contextualArgument_1_0_1\":\"prop value 2\",\"representations\":[{\"__typename\":\"U\",\"id\":\"3\"},{\"__typename\":\"U\",\"id\":\"4\"}]}}", + "message": "couldn't find mock for query {\"query\":\"query QueryLL__Subgraph1__1($representations:[_Any!]!$contextualArgument_1_0:String){_entities(representations:$representations){...on U{field(a:$contextualArgument_1_0)}}}\",\"operationName\":\"QueryLL__Subgraph1__1\",\"variables\":{\"contextualArgument_1_0_1\":\"prop value 2\",\"contextualArgument_1_0_0\":\"prop value 1\",\"representations\":[{\"__typename\":\"U\",\"id\":\"3\"},{\"__typename\":\"U\",\"id\":\"4\"}]}}", "extensions": { "code": "FETCH_ERROR" } diff --git a/apollo-router/tests/snapshots/set_context__set_context_union.snap.new b/apollo-router/tests/snapshots/set_context__set_context_union.snap.new new file mode 100644 index 0000000000..ad86082149 --- /dev/null +++ b/apollo-router/tests/snapshots/set_context__set_context_union.snap.new @@ -0,0 +1,21 @@ +--- +source: apollo-router/tests/set_context.rs +assertion_line: 208 +expression: response +--- +{ + "errors": [ + { + "message": "Cannot query field \"k\" on type \"Query\".", + "locations": [ + { + "line": 2, + "column": 5 + } + ], + "extensions": { + "code": "GRAPHQL_VALIDATION_FAILED" + } + } + ] +} From 2f5e3748c8436ed98dde69fab435494c66574bf6 Mon Sep 17 00:00:00 2001 From: Chris Lenfest Date: Wed, 15 May 2024 20:59:21 -0500 Subject: [PATCH 31/69] refactor done, tests not working yet --- apollo-router/src/json_ext.rs | 2 +- apollo-router/src/query_planner/fetch.rs | 15 +- .../src/query_planner/subgraph_context.rs | 429 ++++++------------ .../tests/fixtures/set_context/one.json | 4 +- .../set_context__set_context.snap.new | 102 ----- ...ontext__set_context_list_of_lists.snap.new | 138 ------ .../set_context__set_context_union.snap.new | 21 - fuzz/Cargo.toml | 2 +- 8 files changed, 161 insertions(+), 552 deletions(-) delete mode 100644 apollo-router/tests/snapshots/set_context__set_context.snap.new delete mode 100644 apollo-router/tests/snapshots/set_context__set_context_list_of_lists.snap.new delete mode 100644 apollo-router/tests/snapshots/set_context__set_context_union.snap.new diff --git a/apollo-router/src/json_ext.rs b/apollo-router/src/json_ext.rs index e0b29f9ed4..b333ec90b1 100644 --- a/apollo-router/src/json_ext.rs +++ b/apollo-router/src/json_ext.rs @@ -1064,7 +1064,7 @@ impl Path { } self.clone() - } + } } impl FromIterator for Path { diff --git a/apollo-router/src/query_planner/fetch.rs b/apollo-router/src/query_planner/fetch.rs index 16a8eadf01..a50fe41168 100644 --- a/apollo-router/src/query_planner/fetch.rs +++ b/apollo-router/src/query_planner/fetch.rs @@ -269,7 +269,6 @@ impl Variables { ) -> Option { let body = request.body(); let mut subgraph_context = SubgraphContext::new(data, current_dir, schema, context_rewrites); - dbg!("new variables"); if !requires.is_empty() { let mut variables = Object::with_capacity(1 + variable_usages.len()); @@ -282,7 +281,6 @@ impl Variables { let mut inverted_paths: Vec> = Vec::new(); let mut values: IndexSet = IndexSet::new(); data.select_values_and_paths(schema, current_dir, |path, value| { - dbg!(&path, &value); // first get contextual values that are required if let Some(context) = subgraph_context.as_mut() { context.execute_on_path(path); @@ -397,9 +395,14 @@ impl FetchNode { }; let aliased_operation = build_operation_with_aliasing(operation, &contextual_arguments, parameters.schema); - if let Ok(a) = aliased_operation { - dbg!(a.serialize().no_indent()); - } + let alias_query_string; // this exists outside the if block to allow the as_str() to be longer lived + let query_str = if let Ok(op) = aliased_operation { + alias_query_string = op.to_string(); + alias_query_string.as_str() + } else { + operation.as_serialized() + }; + let mut subgraph_request = SubgraphRequest::builder() .supergraph_request(parameters.supergraph_request.clone()) .subgraph_request( @@ -418,7 +421,7 @@ impl FetchNode { ) .body( Request::builder() - .query(operation.as_serialized()) + .query(query_str) .and_operation_name(operation_name.as_ref().map(|n| n.to_string())) .variables(variables.clone()) .build(), diff --git a/apollo-router/src/query_planner/subgraph_context.rs b/apollo-router/src/query_planner/subgraph_context.rs index ea67c2f64f..71e119881e 100644 --- a/apollo-router/src/query_planner/subgraph_context.rs +++ b/apollo-router/src/query_planner/subgraph_context.rs @@ -1,19 +1,15 @@ -use std::collections::HashMap; -use std::collections::HashSet; use apollo_compiler::ast; -use apollo_compiler::ast::Document; use apollo_compiler::ast::Name; use apollo_compiler::ast::VariableDefinition; use apollo_compiler::executable; use apollo_compiler::executable::Operation; use apollo_compiler::executable::Selection; use apollo_compiler::executable::SelectionSet; -use apollo_compiler::validation::WithErrors; -use apollo_compiler::ExecutableDocument; use apollo_compiler::Node; -use indexmap::IndexMap; use serde_json_bytes::ByteString; use serde_json_bytes::Map; +use std::collections::HashMap; +use std::collections::HashSet; use super::fetch::SubgraphOperation; use super::rewrites; @@ -24,12 +20,12 @@ use super::rewrites::DataRewrite; use crate::json_ext::Path; use crate::json_ext::PathElement; use crate::json_ext::Value; -// use crate::json_ext::ValueExt; +use crate::json_ext::ValueExt; use crate::spec::Schema; pub(crate) struct ContextualArguments { - pub(crate) arguments: HashSet, // a set of all argument names that will be passed to the subgraph. This is the unmodified name from the query plan - pub(crate) count: usize, // the number of different sets of arguments that exist. This will either be 1 or the number of entities + pub(crate) arguments: HashSet, // a set of all argument names that will be passed to the subgraph. This is the unmodified name from the query plan + pub(crate) count: usize, // the number of different sets of arguments that exist. This will either be 1 or the number of entities } pub(crate) struct SubgraphContext<'a> { @@ -40,68 +36,71 @@ pub(crate) struct SubgraphContext<'a> { pub(crate) named_args: Vec>, } -// TODO: There is probably a function somewhere else that already does this -fn data_at_path<'v>(data: &'v Value, path: &Path) -> Option<&'v Value> { - let v = match &path.0[0] { - PathElement::Fragment(s) => { - // get the value at data.get("__typename") and compare it with s. If the values are equal, return data, otherwise None - let mut z: Option<&Value> = None; - let wrapped_typename = data.get("__typename"); - if let Some(t) = wrapped_typename { - if t.as_str() == Some(s.as_str()) { - z = Some(data); - } - } - z - } - PathElement::Key(v, _) => data.get(v), - PathElement::Index(idx) => Some(&data[idx]), - PathElement::Flatten(_) => None, - }; - - if path.len() > 1 { - if let Some(val) = v { - let remaining_path = path.iter().skip(1).cloned().collect(); - return data_at_path(val, &remaining_path); - } - } - v -} +// TODO: We're using ValueExt::get_path, so I believe this is no longer needed, but I'm +// going to keep it commented until all the tests are passing +// fn data_at_path<'v>(data: &'v Value, path: &Path) -> Option<&'v Value> { +// let v = match &path.0[0] { +// PathElement::Fragment(s) => { +// // get the value at data.get("__typename") and compare it with s. If the values are equal, return data, otherwise None +// let mut result: Option<&Value> = None; +// let wrapped_typename = data.get("__typename"); +// if let Some(t) = wrapped_typename { +// if t.as_str() == Some(s.as_str()) { +// result = Some(data); +// } +// } +// result +// } +// PathElement::Key(v, _) => data.get(v), +// PathElement::Index(idx) => Some(&data[idx]), +// PathElement::Flatten(_) => None, +// }; + +// if path.len() > 1 { +// if let Some(val) = v { +// let remaining_path = path.iter().skip(1).cloned().collect(); +// return data_at_path(val, &remaining_path); +// } +// } +// v +// } +// context_path is a non-standard relative path which may navigate up the tree +// from the current position. This is indicated with a ".." PathElement::Key +// note that the return value is an absolute path that may be used anywhere fn merge_context_path(current_dir: &Path, context_path: &Path) -> Path { - let mut i = 0; - let mut j = current_dir.len(); - // iterate over the context_path(i), every time we encounter a '..', we want - // to go up one level in the current_dir(j) - while i < context_path.len() { - match &context_path.0.get(i) { - Some(PathElement::Key(e, _)) => { - let mut found = false; - if e == ".." { - while !found { - j -= 1; - if let Some(PathElement::Key(_, _)) = current_dir.0.get(j) { - found = true; - } - } - i += 1; - } else { - break; - } - } - _ => break, - } - } + let mut i = 0; + let mut j = current_dir.len(); + // iterate over the context_path(i), every time we encounter a '..', we want + // to go up one level in the current_dir(j) + while i < context_path.len() { + match &context_path.0.get(i) { + Some(PathElement::Key(e, _)) => { + let mut found = false; + if e == ".." { + while !found { + j -= 1; + if let Some(PathElement::Key(_, _)) = current_dir.0.get(j) { + found = true; + } + } + i += 1; + } else { + break; + } + } + _ => break, + } + } - let mut return_path: Vec = current_dir.iter().take(j).cloned().collect(); + let mut return_path: Vec = current_dir.iter().take(j).cloned().collect(); - context_path.iter().skip(i).for_each(|e| { - return_path.push(e.clone()); - }); - Path(return_path.into_iter().collect()) + context_path.iter().skip(i).for_each(|e| { + return_path.push(e.clone()); + }); + Path(return_path.into_iter().collect()) } - impl<'a> SubgraphContext<'a> { pub(crate) fn new( data: &'a Value, @@ -117,77 +116,70 @@ impl<'a> SubgraphContext<'a> { schema, context_rewrites: rewrites, named_args: Vec::new(), - }) + }); } } None } - - pub(crate) fn execute_on_path( - &mut self, - path: &Path, - ) { + + pub(crate) fn execute_on_path(&mut self, path: &Path) { let mut found_rewrites: HashSet = HashSet::new(); - let hash_map: HashMap = self.context_rewrites + let hash_map: HashMap = self + .context_rewrites .iter() .filter_map(|rewrite| { - match rewrite { - DataRewrite::KeyRenamer(item) => { - if !found_rewrites.contains(item.rename_key_to.as_str()) { - let data_path = merge_context_path(path, &item.path); - let val = data_at_path(self.data, &data_path); - if let Some(v) = val { - // add to found - found_rewrites - .insert(item.rename_key_to.clone().to_string()); - // TODO: not great - let mut new_value = v.clone(); - if let Some(values) = new_value.as_array_mut() { - for v in values { + match rewrite { + DataRewrite::KeyRenamer(item) => { + if !found_rewrites.contains(item.rename_key_to.as_str()) { + let data_path = merge_context_path(path, &item.path); + let val = self.data.get_path(self.schema, &data_path); + + if let Ok(v) = val { + // add to found + found_rewrites.insert(item.rename_key_to.clone().to_string()); + // TODO: not great + let mut new_value = v.clone(); + if let Some(values) = new_value.as_array_mut() { + for v in values { + rewrites::apply_single_rewrite( + self.schema, + v, + &DataRewrite::KeyRenamer({ + DataKeyRenamer { + path: data_path.clone(), + rename_key_to: item.rename_key_to.clone(), + } + }), + ); + } + } else { rewrites::apply_single_rewrite( self.schema, - v, + &mut new_value, &DataRewrite::KeyRenamer({ DataKeyRenamer { - path: data_path.clone(), - rename_key_to: item - .rename_key_to - .clone(), + path: data_path, + rename_key_to: item.rename_key_to.clone(), } }), ); } - } else { - rewrites::apply_single_rewrite( - self.schema, - &mut new_value, - &DataRewrite::KeyRenamer({ - DataKeyRenamer { - path: data_path, - rename_key_to: item.rename_key_to.clone(), - } - }), - ); + return Some((item.rename_key_to.to_string(), new_value)); } - return Some((item.rename_key_to.to_string(), new_value)); } + None } - None - }, - DataRewrite::ValueSetter(_) => { - None + DataRewrite::ValueSetter(_) => None, } - } }) .collect(); self.named_args.push(hash_map); } - + pub(crate) fn add_variables_and_get_args( &self, variables: &mut Map, ) -> Option { - dbg!(&self.named_args); let (extended_vars, contextual_args) = if let Some(first_map) = self.named_args.first() { if self.named_args.iter().all(|map| map == first_map) { ( @@ -208,115 +200,89 @@ impl<'a> SubgraphContext<'a> { (new_named_param, v.clone()) })); } - (hash_map, Some(ContextualArguments{ - arguments: arg_names, - count: self.named_args.len(), - })) + ( + hash_map, + Some(ContextualArguments { + arguments: arg_names, + count: self.named_args.len(), + }), + ) } } else { (HashMap::new(), None) }; - + variables.extend( extended_vars .iter() .map(|(key, value)| (key.as_str().into(), value.clone())), ); - + contextual_args } } -// fn add_index_alias_to_first_selection( -// selection_set: &SelectionSet, -// index: usize, -// ) -> Result { -// let selection = selection_set.selections.first(); -// if let Some(executable::Selection::Field(node)) = selection { -// let z: Selection = Node::new(executable::Field { -// definition: node.definition.clone(), -// alias: node.alias.clone(), -// name: node.name.clone(), -// arguments: node.arguments.clone(), -// directives: node.directives.clone(), -// selection_set: node.selection_set.clone(), -// }); -// Ok(z) -// } else { -// Err(ContextBatchingError::NoSelectionSet) -// } -// } - -pub fn build_operation_with_aliasing( +pub(crate) fn build_operation_with_aliasing( subgraph_operation: &SubgraphOperation, contextual_arguments: &Option, schema: &Schema, ) -> Result { let mut selections: Vec = vec![]; match contextual_arguments { - Some(ContextualArguments { arguments, count }) => { - dbg!("arguments", arguments); + Some(ContextualArguments { arguments, count }) => { let parsed_document = subgraph_operation.as_parsed(schema.supergraph_schema()); if let Ok(document) = parsed_document { // TODO: Can there be more than one named operation? // Can there be an anonymous operation? - // let mut new_variables: Vec> = vec![]; - // let mut new_selection_set: Vec = vec![]; - // let mut operations: Vec = vec![]; - if let Some((name, op)) = document.named_operations.first() { + if let Some((_, op)) = document.named_operations.first() { let mut new_variables: Vec> = vec![]; op.variables.iter().for_each(|v| { if arguments.contains(v.name.as_str()) { for i in 0..*count { - new_variables.push( - Node::new(VariableDefinition { - name: Name::new_unchecked(format!("{}_{}", v.name.as_str(), i).into()), - ty: v.ty.clone(), - default_value: v.default_value.clone(), - directives: v.directives.clone(), - }) - ); + new_variables.push(Node::new(VariableDefinition { + name: Name::new_unchecked( + format!("{}_{}", v.name.as_str(), i).into(), + ), + ty: v.ty.clone(), + default_value: v.default_value.clone(), + directives: v.directives.clone(), + })); } } else { new_variables.push(v.clone()); } }); - + for i in 0..*count { // If we are aliasing, we know that there is only one selection in the top level SelectionSet // it is a field selection for _entities, so it's ok to reach in and give it an alias let mut selection_set = op.selection_set.clone(); transform_selection_set(&mut selection_set, arguments, i, true); selections.push(selection_set.selections[0].clone()) - }; + } - Ok( - Operation { - operation_type: op.operation_type.clone(), - name: op.name.clone(), - directives: op.directives.clone(), - variables: new_variables, - selection_set: SelectionSet { - ty: op.selection_set.ty.clone(), - selections, - }, - } - ) + Ok(Operation { + operation_type: op.operation_type.clone(), + name: op.name.clone(), + directives: op.directives.clone(), + variables: new_variables, + selection_set: SelectionSet { + ty: op.selection_set.ty.clone(), + selections, + }, + }) } else { Err(ContextBatchingError::NoSelectionSet) } } else { Err(ContextBatchingError::NoSelectionSet) } - }, + } None => Err(ContextBatchingError::NoSelectionSet), } } -fn add_alias_to_selection( - selection: &mut executable::Field, - index: usize, -) { +fn add_alias_to_selection(selection: &mut executable::Field, index: usize) { selection.alias = Some(Name::new_unchecked(format!("_{}", index).into())); } @@ -326,8 +292,10 @@ fn transform_selection_set( index: usize, add_alias: bool, // at the top level, we'll add an alias to field selections ) { - selection_set.selections.iter_mut().for_each(|selection| { - match selection { + selection_set + .selections + .iter_mut() + .for_each(|selection| match selection { executable::Selection::Field(node) => { let node = node.make_mut(); transform_field_arguments(&mut node.arguments, arguments, index); @@ -335,14 +303,13 @@ fn transform_selection_set( if add_alias { add_alias_to_selection(node, index); } - }, + } executable::Selection::InlineFragment(node) => { let node = node.make_mut(); transform_selection_set(&mut node.selection_set, arguments, index, false); - }, + } _ => (), - } - }); + }); } fn transform_field_arguments( @@ -350,125 +317,25 @@ fn transform_field_arguments( arguments: &HashSet, index: usize, ) { - dbg!("scanning arguments"); arguments_in_selection.iter_mut().for_each(|arg| { let arg = arg.make_mut(); if let Some(v) = arg.value.as_variable() { if arguments.contains(v.as_str()) { - arg.value = Node::new(ast::Value::Variable(Name::new_unchecked(format!("{}_{}", v.as_str(), index).into()))); + arg.value = Node::new(ast::Value::Variable(Name::new_unchecked( + format!("{}_{}", v.as_str(), index).into(), + ))); } } }); } - -// // to delete -// fn query_batching_for_contextual_args( -// operation: &str, -// contextual_arguments: &Option, -// ) -> Result, ContextBatchingError> { -// if let Some(ContextualArguments { arguments, count }) = contextual_arguments { -// let parser = apollo_compiler::Parser::new() -// .parse_ast(operation, "") -// .map_err(ContextBatchingError::InvalidDocument)?; -// if let Some(mut operation) = parser -// .definitions -// .into_iter() -// .find_map(|definition| definition.as_operation_definition().cloned()) -// { -// let mut new_variables = vec![]; -// if operation -// .variables -// .iter() -// .any(|v| arguments.contains(v.name.as_str())) -// { -// let new_selection_set: Vec<_> = (0..*count) -// .map(|i| { -// // TODO: Unwrap -// let mut s = operation -// .selection_set -// .first() -// .ok_or_else(|| ContextBatchingError::NoSelectionSet)? -// .clone(); -// if let ast::Selection::Field(f) = &mut s { -// let f = f.make_mut(); -// f.alias = Some(Name::new_unchecked(format!("_{}", i).into())); -// } - -// for v in &operation.variables { -// if arguments.contains(v.name.as_str()) { -// let mut cloned = v.clone(); -// let new_variable = cloned.make_mut(); -// new_variable.name = -// Name::new_unchecked(format!("{}_{}", v.name, i).into()); -// new_variables.push(Node::new(new_variable.clone())); - -// s = rename_variables(s, v.name.clone(), new_variable.name.clone()); -// } else if !new_variables.iter().any(|var| var.name == v.name) { -// new_variables.push(v.clone()); -// } -// } - -// Ok(s) -// }) -// .collect::, _>>()?; - -// let new_operation = operation.make_mut(); -// new_operation.selection_set = new_selection_set; -// new_operation.variables = new_variables; - -// return Ok(Some(new_operation.serialize().no_indent().to_string())); -// } -// } -// } - -// Ok(None) -// } - -// fn rename_variables(selection_set: ast::Selection, from: Name, to: Name) -> ast::Selection { -// match selection_set { -// ast::Selection::Field(f) => { -// let mut new = f.clone(); - -// let as_mut = new.make_mut(); - -// as_mut.arguments.iter_mut().for_each(|arg| { -// if arg.value.as_variable() == Some(&from) { -// arg.make_mut().value = ast::Value::Variable(to.clone()).into(); -// } -// }); - -// as_mut.selection_set = as_mut -// .selection_set -// .clone() -// .into_iter() -// .map(|s| rename_variables(s, from.clone(), to.clone())) -// .collect(); - -// ast::Selection::Field(new) -// } -// ast::Selection::InlineFragment(f) => { -// let mut new = f.clone(); -// new.make_mut().selection_set = f -// .selection_set -// .clone() -// .into_iter() -// .map(|s| rename_variables(s, from.clone(), to.clone())) -// .collect(); -// ast::Selection::InlineFragment(new) -// } -// ast::Selection::FragmentSpread(f) => ast::Selection::FragmentSpread(f), -// } -// } - #[derive(Debug)] -pub enum ContextBatchingError { - InvalidDocument(WithErrors), +pub(crate) enum ContextBatchingError { NoSelectionSet, } -// #[derive(Debug)] -// #[test] +#[test] +fn test_merge_context_path() {} // fn test_query_batching_for_contextual_args() { // let old_query = "query QueryLL__Subgraph1__1($representations:[_Any!]!$Subgraph1_U_field_a:String!){_entities(representations:$representations){...on U{id field(a:$Subgraph1_U_field_a)}}}"; // let mut ctx_args = HashSet::new(); diff --git a/apollo-router/tests/fixtures/set_context/one.json b/apollo-router/tests/fixtures/set_context/one.json index e9bc0e49f3..79271c87ec 100644 --- a/apollo-router/tests/fixtures/set_context/one.json +++ b/apollo-router/tests/fixtures/set_context/one.json @@ -193,7 +193,7 @@ }, { "request": { - "query": "query QueryLL__Subgraph1__1($representations: [_Any!]!, $contextualArgument_1_0_0: String!, $contextualArgument_1_0_1: String!) { _0: _entities(representations: $representations) { ... on U { field(a: $contextualArgument_1_0_0) } } _1: _entities(representations: $representations) { ... on U { field(a: $contextualArgument_1_0_1) } } }", + "query": "query QueryLL__Subgraph1__1($representations: [_Any!]!, $contextualArgument_1_0_0: String, $contextualArgument_1_0_1: String) {\\n _0: _entities(representations: $representations) {\\n ... on U {\\n field(a: $contextualArgument_1_0_0)\\n }\\n }\\n _1: _entities(representations: $representations) {\\n ... on U {\\n field(a: $contextualArgument_1_0_1)\\n }\\n }\\n}", "operationName": "QueryLL__Subgraph1__1", "variables": { "contextualArgument_1_0_0": "prop value 1", @@ -221,7 +221,7 @@ }, { "request": { - "query": "query QueryLL__Subgraph1__1($representations: [_Any!]!, $contextualArgument_1_0_0: String!, $contextualArgument_1_0_1: String!) { _0: _entities(representations: $representations) { ... on U { field(a: $contextualArgument_1_0_0) } } _1: _entities(representations: $representations) { ... on U { field(a: $contextualArgument_1_0_1) } } }", + "query": "query QueryLL__Subgraph1__1($representations: [_Any!]!, $contextualArgument_1_0_0: String, $contextualArgument_1_0_1: String) {\\n _0: _entities(representations: $representations) {\\n ... on U {\\n field(a: $contextualArgument_1_0_0)\\n }\\n }\\n _1: _entities(representations: $representations) {\\n ... on U {\\n field(a: $contextualArgument_1_0_1)\\n }\\n }\\n}", "operationName": "QueryLL__Subgraph1__1", "variables": { "contextualArgument_1_0_1": "prop value 2", diff --git a/apollo-router/tests/snapshots/set_context__set_context.snap.new b/apollo-router/tests/snapshots/set_context__set_context.snap.new deleted file mode 100644 index e1c7b6d5ee..0000000000 --- a/apollo-router/tests/snapshots/set_context__set_context.snap.new +++ /dev/null @@ -1,102 +0,0 @@ ---- -source: apollo-router/tests/set_context.rs -assertion_line: 60 -expression: response ---- -{ - "data": { - "t": { - "__typename": "T", - "id": "1", - "u": { - "__typename": "U", - "field": 1234 - } - } - }, - "extensions": { - "apolloQueryPlan": { - "object": { - "kind": "QueryPlan", - "node": { - "kind": "Sequence", - "nodes": [ - { - "kind": "Fetch", - "serviceName": "Subgraph1", - "variableUsages": [], - "operation": "query Query__Subgraph1__0{t{__typename prop id u{__typename id}}}", - "operationName": "Query__Subgraph1__0", - "operationKind": "query", - "id": null, - "inputRewrites": null, - "outputRewrites": null, - "contextRewrites": null, - "schemaAwareHash": "d7cb2d1809789d49360ca0a60570555f83855f00547675f366915c9d9d90fef9", - "authorization": { - "is_authenticated": false, - "scopes": [], - "policies": [] - } - }, - { - "kind": "Flatten", - "path": [ - "", - "t", - "u" - ], - "node": { - "kind": "Fetch", - "serviceName": "Subgraph1", - "requires": [ - { - "kind": "InlineFragment", - "typeCondition": "U", - "selections": [ - { - "kind": "Field", - "name": "__typename" - }, - { - "kind": "Field", - "name": "id" - } - ] - } - ], - "variableUsages": [ - "contextualArgument_1_0" - ], - "operation": "query Query__Subgraph1__1($representations:[_Any!]!$contextualArgument_1_0:String){_entities(representations:$representations){...on U{field(a:$contextualArgument_1_0)}}}", - "operationName": "Query__Subgraph1__1", - "operationKind": "query", - "id": null, - "inputRewrites": null, - "outputRewrites": null, - "contextRewrites": [ - { - "kind": "KeyRenamer", - "path": [ - "..", - "... on T", - "prop" - ], - "renameKeyTo": "contextualArgument_1_0" - } - ], - "schemaAwareHash": "66b954f39aead8436321c671eb71e56ce15bbe0c7b82f06b2f8f70473ce1cb6e", - "authorization": { - "is_authenticated": false, - "scopes": [], - "policies": [] - } - } - } - ] - } - }, - "text": "QueryPlan {\n Sequence {\n Fetch(service: \"Subgraph1\") {\n {\n t {\n __typename\n prop\n id\n u {\n __typename\n id\n }\n }\n }\n },\n Flatten(path: \".t.u\") {\n Fetch(service: \"Subgraph1\") {\n {\n ... on U {\n __typename\n id\n }\n } =>\n {\n ... on U {\n field(a: $contextualArgument_1_0)\n }\n }\n },\n },\n },\n}" - } - } -} diff --git a/apollo-router/tests/snapshots/set_context__set_context_list_of_lists.snap.new b/apollo-router/tests/snapshots/set_context__set_context_list_of_lists.snap.new deleted file mode 100644 index dbf01c629a..0000000000 --- a/apollo-router/tests/snapshots/set_context__set_context_list_of_lists.snap.new +++ /dev/null @@ -1,138 +0,0 @@ ---- -source: apollo-router/tests/set_context.rs -assertion_line: 171 -expression: response ---- -{ - "data": { - "tList": [ - { - "id": "1", - "uList": [ - null - ] - }, - { - "id": "2", - "uList": [ - null - ] - } - ] - }, - "errors": [ - { - "message": "couldn't find mock for query {\"query\":\"query QueryLL__Subgraph1__1($representations:[_Any!]!$contextualArgument_1_0:String){_entities(representations:$representations){...on U{field(a:$contextualArgument_1_0)}}}\",\"operationName\":\"QueryLL__Subgraph1__1\",\"variables\":{\"contextualArgument_1_0_1\":\"prop value 2\",\"contextualArgument_1_0_0\":\"prop value 1\",\"representations\":[{\"__typename\":\"U\",\"id\":\"3\"},{\"__typename\":\"U\",\"id\":\"4\"}]}}", - "extensions": { - "code": "FETCH_ERROR" - } - } - ], - "extensions": { - "valueCompletion": [ - { - "message": "Cannot return null for non-nullable field U.field", - "path": [ - "tList", - 0, - "uList", - 0 - ] - }, - { - "message": "Cannot return null for non-nullable field U.field", - "path": [ - "tList", - 1, - "uList", - 0 - ] - } - ], - "apolloQueryPlan": { - "object": { - "kind": "QueryPlan", - "node": { - "kind": "Sequence", - "nodes": [ - { - "kind": "Fetch", - "serviceName": "Subgraph1", - "variableUsages": [], - "operation": "query QueryLL__Subgraph1__0{tList{prop id uList{__typename id}}}", - "operationName": "QueryLL__Subgraph1__0", - "operationKind": "query", - "id": null, - "inputRewrites": null, - "outputRewrites": null, - "contextRewrites": null, - "schemaAwareHash": "20170491dab082e15802bd0b9e6abdf2c6803589cc969668e8b0bc8388af0542", - "authorization": { - "is_authenticated": false, - "scopes": [], - "policies": [] - } - }, - { - "kind": "Flatten", - "path": [ - "", - "tList", - "@", - "uList", - "@" - ], - "node": { - "kind": "Fetch", - "serviceName": "Subgraph1", - "requires": [ - { - "kind": "InlineFragment", - "typeCondition": "U", - "selections": [ - { - "kind": "Field", - "name": "__typename" - }, - { - "kind": "Field", - "name": "id" - } - ] - } - ], - "variableUsages": [ - "contextualArgument_1_0" - ], - "operation": "query QueryLL__Subgraph1__1($representations:[_Any!]!$contextualArgument_1_0:String){_entities(representations:$representations){...on U{field(a:$contextualArgument_1_0)}}}", - "operationName": "QueryLL__Subgraph1__1", - "operationKind": "query", - "id": null, - "inputRewrites": null, - "outputRewrites": null, - "contextRewrites": [ - { - "kind": "KeyRenamer", - "path": [ - "..", - "... on T", - "prop" - ], - "renameKeyTo": "contextualArgument_1_0" - } - ], - "schemaAwareHash": "a9b24549250c12e38c398c32e9218134fab000be3b934ebc6bb38ea096343646", - "authorization": { - "is_authenticated": false, - "scopes": [], - "policies": [] - } - } - } - ] - } - }, - "text": "QueryPlan {\n Sequence {\n Fetch(service: \"Subgraph1\") {\n {\n tList {\n prop\n id\n uList {\n __typename\n id\n }\n }\n }\n },\n Flatten(path: \".tList.@.uList.@\") {\n Fetch(service: \"Subgraph1\") {\n {\n ... on U {\n __typename\n id\n }\n } =>\n {\n ... on U {\n field(a: $contextualArgument_1_0)\n }\n }\n },\n },\n },\n}" - } - } -} diff --git a/apollo-router/tests/snapshots/set_context__set_context_union.snap.new b/apollo-router/tests/snapshots/set_context__set_context_union.snap.new deleted file mode 100644 index ad86082149..0000000000 --- a/apollo-router/tests/snapshots/set_context__set_context_union.snap.new +++ /dev/null @@ -1,21 +0,0 @@ ---- -source: apollo-router/tests/set_context.rs -assertion_line: 208 -expression: response ---- -{ - "errors": [ - { - "message": "Cannot query field \"k\" on type \"Query\".", - "locations": [ - { - "line": 2, - "column": 5 - } - ], - "extensions": { - "code": "GRAPHQL_VALIDATION_FAILED" - } - } - ] -} diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index 8151da867a..ace1dead0a 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -20,7 +20,7 @@ reqwest = { workspace = true, features = ["json", "blocking"] } serde_json.workspace = true tokio.workspace = true # note: this dependency should _always_ be pinned, prefix the version with an `=` -router-bridge = "=0.5.21+v2.7.5" +router-bridge = { git = "https://github.com/apollographql/federation-rs", rev = "518ce143088f84575e2c64436985802a25458a13" } [dev-dependencies] anyhow = "1" From b69175f1316ecc6fd8a5520846bfc182844d8886 Mon Sep 17 00:00:00 2001 From: Chris Lenfest Date: Wed, 15 May 2024 22:02:43 -0500 Subject: [PATCH 32/69] updating tests to be more DRY and snapshots --- apollo-router/tests/set_context.rs | 138 +++++------------- .../snapshots/set_context__set_context.snap | 7 +- .../set_context__set_context_list.snap.new | 137 +++++++++++++++++ ...set_context__set_context_no_typenames.snap | 5 +- 4 files changed, 177 insertions(+), 110 deletions(-) create mode 100644 apollo-router/tests/snapshots/set_context__set_context_list.snap.new diff --git a/apollo-router/tests/set_context.rs b/apollo-router/tests/set_context.rs index 7ed213ce72..d6387a95a8 100644 --- a/apollo-router/tests/set_context.rs +++ b/apollo-router/tests/set_context.rs @@ -23,8 +23,10 @@ struct RequestAndResponse { response: Response, } -#[tokio::test(flavor = "multi_thread")] -async fn test_set_context() { +async fn run_single_request( + query: &str, + mocks: &[(&'static str, &'static str)], +) -> Response { let harness = setup_from_mocks( json! {{ "experimental_type_conditioned_fetching": true, @@ -36,139 +38,65 @@ async fn test_set_context() { "all": true } }}, - &[ - ("Subgraph1", include_str!("fixtures/set_context/one.json")), - ("Subgraph2", include_str!("fixtures/set_context/two.json")), - ], + mocks, ); let supergraph_service = harness.build_supergraph().await.unwrap(); let request = supergraph::Request::fake_builder() - .query(QUERY.to_string()) + .query(query.to_string()) .header("Apollo-Expose-Query-Plan", "true") .variables(Default::default()) .build() .expect("expecting valid request"); - let response = supergraph_service + supergraph_service .oneshot(request) .await .unwrap() .next_response() .await - .unwrap(); + .unwrap() +} +#[tokio::test(flavor = "multi_thread")] +async fn test_set_context() { + let response = run_single_request(QUERY, &[ + ("Subgraph1", include_str!("fixtures/set_context/one.json")), + ("Subgraph2", include_str!("fixtures/set_context/two.json")), + ]).await; + insta::assert_json_snapshot!(response); } #[tokio::test(flavor = "multi_thread")] async fn test_set_context_no_typenames() { - let harness = setup_from_mocks( - json! {{ - "experimental_type_conditioned_fetching": true, - // will make debugging easier - "plugins": { - "experimental.expose_query_plan": true - }, - "include_subgraph_errors": { - "all": true - } - }}, - &[ - ("Subgraph1", include_str!("fixtures/set_context/one.json")), - ("Subgraph2", include_str!("fixtures/set_context/two.json")), - ], - ); - let supergraph_service = harness.build_supergraph().await.unwrap(); - let request = supergraph::Request::fake_builder() - .query(QUERY_NO_TYPENAMES.to_string()) - .header("Apollo-Expose-Query-Plan", "true") - .variables(Default::default()) - .build() - .expect("expecting valid request"); - - let response = supergraph_service - .oneshot(request) - .await - .unwrap() - .next_response() - .await - .unwrap(); - + let response = run_single_request(QUERY_NO_TYPENAMES, &[ + ("Subgraph1", include_str!("fixtures/set_context/one.json")), + ("Subgraph2", include_str!("fixtures/set_context/two.json")), + ]).await; + insta::assert_json_snapshot!(response); + } #[tokio::test(flavor = "multi_thread")] async fn test_set_context_list() { - let harness = setup_from_mocks( - json! {{ - "experimental_type_conditioned_fetching": true, - // will make debugging easier - "plugins": { - "experimental.expose_query_plan": true - }, - "include_subgraph_errors": { - "all": true - } - }}, - &[ - ("Subgraph1", include_str!("fixtures/set_context/one.json")), - ("Subgraph2", include_str!("fixtures/set_context/two.json")), - ], - ); - let supergraph_service = harness.build_supergraph().await.unwrap(); - let request = supergraph::Request::fake_builder() - .query(QUERY_WITH_LIST.to_string()) - .header("Apollo-Expose-Query-Plan", "true") - .variables(Default::default()) - .build() - .expect("expecting valid request"); - - let response = supergraph_service - .oneshot(request) - .await - .unwrap() - .next_response() - .await - .unwrap(); - + let response = run_single_request(QUERY_WITH_LIST, &[ + ("Subgraph1", include_str!("fixtures/set_context/one.json")), + ("Subgraph2", include_str!("fixtures/set_context/two.json")), + ]).await; + insta::assert_json_snapshot!(response); } #[tokio::test(flavor = "multi_thread")] async fn test_set_context_list_of_lists() { - let harness = setup_from_mocks( - json! {{ - "experimental_type_conditioned_fetching": true, - // will make debugging easier - "plugins": { - "experimental.expose_query_plan": true - }, - "include_subgraph_errors": { - "all": true - } - }}, - &[ - ("Subgraph1", include_str!("fixtures/set_context/one.json")), - ("Subgraph2", include_str!("fixtures/set_context/two.json")), - ], - ); - let supergraph_service = harness.build_supergraph().await.unwrap(); - let request: supergraph::Request = supergraph::Request::fake_builder() - .query(QUERY_WITH_LIST_OF_LISTS.to_string()) - .header("Apollo-Expose-Query-Plan", "true") - .variables(Default::default()) - .build() - .expect("expecting valid request"); - - let response = supergraph_service - .oneshot(request) - .await - .unwrap() - .next_response() - .await - .unwrap(); - + let response = run_single_request(QUERY_WITH_LIST_OF_LISTS, &[ + ("Subgraph1", include_str!("fixtures/set_context/one.json")), + ("Subgraph2", include_str!("fixtures/set_context/two.json")), + ]).await; + insta::assert_json_snapshot!(response); + } #[tokio::test(flavor = "multi_thread")] diff --git a/apollo-router/tests/snapshots/set_context__set_context.snap b/apollo-router/tests/snapshots/set_context__set_context.snap index 17c6ede366..79c4e26501 100644 --- a/apollo-router/tests/snapshots/set_context__set_context.snap +++ b/apollo-router/tests/snapshots/set_context__set_context.snap @@ -67,7 +67,7 @@ expression: response "variableUsages": [ "contextualArgument_1_0" ], - "operation": "query Query__Subgraph1__1($representations:[_Any!]!$contextualArgument_1_0:String!){_entities(representations:$representations){...on U{__typename field(a:$contextualArgument_1_0)}}}", + "operation": "query Query__Subgraph1__1($representations:[_Any!]!$contextualArgument_1_0:String){_entities(representations:$representations){...on U{field(a:$contextualArgument_1_0)}}}", "operationName": "Query__Subgraph1__1", "operationKind": "query", "id": null, @@ -78,12 +78,13 @@ expression: response "kind": "KeyRenamer", "path": [ "..", + "... on T", "prop" ], "renameKeyTo": "contextualArgument_1_0" } ], - "schemaAwareHash": "ef0e5caa045bd7e162c6003f2152476e70e017aff6e925857fa2a2def13f11d4", + "schemaAwareHash": "66b954f39aead8436321c671eb71e56ce15bbe0c7b82f06b2f8f70473ce1cb6e", "authorization": { "is_authenticated": false, "scopes": [], @@ -94,7 +95,7 @@ expression: response ] } }, - "text": "QueryPlan {\n Sequence {\n Fetch(service: \"Subgraph1\") {\n {\n t {\n __typename\n prop\n id\n u {\n __typename\n id\n }\n }\n }\n },\n Flatten(path: \".t.u\") {\n Fetch(service: \"Subgraph1\") {\n {\n ... on U {\n __typename\n id\n }\n } =>\n {\n ... on U {\n __typename\n field(a: $contextualArgument_1_0)\n }\n }\n },\n },\n },\n}" + "text": "QueryPlan {\n Sequence {\n Fetch(service: \"Subgraph1\") {\n {\n t {\n __typename\n prop\n id\n u {\n __typename\n id\n }\n }\n }\n },\n Flatten(path: \".t.u\") {\n Fetch(service: \"Subgraph1\") {\n {\n ... on U {\n __typename\n id\n }\n } =>\n {\n ... on U {\n field(a: $contextualArgument_1_0)\n }\n }\n },\n },\n },\n}" } } } diff --git a/apollo-router/tests/snapshots/set_context__set_context_list.snap.new b/apollo-router/tests/snapshots/set_context__set_context_list.snap.new new file mode 100644 index 0000000000..65b30fa408 --- /dev/null +++ b/apollo-router/tests/snapshots/set_context__set_context_list.snap.new @@ -0,0 +1,137 @@ +--- +source: apollo-router/tests/set_context.rs +assertion_line: 88 +expression: response +--- +{ + "data": { + "t": { + "id": "1", + "uList": [ + null, + null, + null + ] + } + }, + "errors": [ + { + "message": "couldn't find mock for query {\"query\":\"query Query__Subgraph1__1($representations:[_Any!]!$contextualArgument_1_0:String){_entities(representations:$representations){...on U{field(a:$contextualArgument_1_0)}}}\",\"operationName\":\"Query__Subgraph1__1\",\"variables\":{\"contextualArgument_1_0\":\"prop value\",\"representations\":[{\"__typename\":\"U\",\"id\":\"1\"},{\"__typename\":\"U\",\"id\":\"2\"},{\"__typename\":\"U\",\"id\":\"3\"}]}}", + "extensions": { + "code": "FETCH_ERROR" + } + } + ], + "extensions": { + "valueCompletion": [ + { + "message": "Cannot return null for non-nullable field U.field", + "path": [ + "t", + "uList", + 0 + ] + }, + { + "message": "Cannot return null for non-nullable field U.field", + "path": [ + "t", + "uList", + 1 + ] + }, + { + "message": "Cannot return null for non-nullable field U.field", + "path": [ + "t", + "uList", + 2 + ] + } + ], + "apolloQueryPlan": { + "object": { + "kind": "QueryPlan", + "node": { + "kind": "Sequence", + "nodes": [ + { + "kind": "Fetch", + "serviceName": "Subgraph1", + "variableUsages": [], + "operation": "query Query__Subgraph1__0{t{prop id uList{__typename id}}}", + "operationName": "Query__Subgraph1__0", + "operationKind": "query", + "id": null, + "inputRewrites": null, + "outputRewrites": null, + "contextRewrites": null, + "schemaAwareHash": "904cce765299d4ec167658b15259c8bc8c3093af5bb33db4d2979827472646cd", + "authorization": { + "is_authenticated": false, + "scopes": [], + "policies": [] + } + }, + { + "kind": "Flatten", + "path": [ + "", + "t", + "uList", + "@" + ], + "node": { + "kind": "Fetch", + "serviceName": "Subgraph1", + "requires": [ + { + "kind": "InlineFragment", + "typeCondition": "U", + "selections": [ + { + "kind": "Field", + "name": "__typename" + }, + { + "kind": "Field", + "name": "id" + } + ] + } + ], + "variableUsages": [ + "contextualArgument_1_0" + ], + "operation": "query Query__Subgraph1__1($representations:[_Any!]!$contextualArgument_1_0:String){_entities(representations:$representations){...on U{field(a:$contextualArgument_1_0)}}}", + "operationName": "Query__Subgraph1__1", + "operationKind": "query", + "id": null, + "inputRewrites": null, + "outputRewrites": null, + "contextRewrites": [ + { + "kind": "KeyRenamer", + "path": [ + "..", + "... on T", + "prop" + ], + "renameKeyTo": "contextualArgument_1_0" + } + ], + "schemaAwareHash": "66b954f39aead8436321c671eb71e56ce15bbe0c7b82f06b2f8f70473ce1cb6e", + "authorization": { + "is_authenticated": false, + "scopes": [], + "policies": [] + } + } + } + ] + } + }, + "text": "QueryPlan {\n Sequence {\n Fetch(service: \"Subgraph1\") {\n {\n t {\n prop\n id\n uList {\n __typename\n id\n }\n }\n }\n },\n Flatten(path: \".t.uList.@\") {\n Fetch(service: \"Subgraph1\") {\n {\n ... on U {\n __typename\n id\n }\n } =>\n {\n ... on U {\n field(a: $contextualArgument_1_0)\n }\n }\n },\n },\n },\n}" + } + } +} diff --git a/apollo-router/tests/snapshots/set_context__set_context_no_typenames.snap b/apollo-router/tests/snapshots/set_context__set_context_no_typenames.snap index 0d0a17919b..b4dee09d70 100644 --- a/apollo-router/tests/snapshots/set_context__set_context_no_typenames.snap +++ b/apollo-router/tests/snapshots/set_context__set_context_no_typenames.snap @@ -65,7 +65,7 @@ expression: response "variableUsages": [ "contextualArgument_1_0" ], - "operation": "query Query__Subgraph1__1($representations:[_Any!]!$contextualArgument_1_0:String!){_entities(representations:$representations){...on U{field(a:$contextualArgument_1_0)}}}", + "operation": "query Query__Subgraph1__1($representations:[_Any!]!$contextualArgument_1_0:String){_entities(representations:$representations){...on U{field(a:$contextualArgument_1_0)}}}", "operationName": "Query__Subgraph1__1", "operationKind": "query", "id": null, @@ -76,12 +76,13 @@ expression: response "kind": "KeyRenamer", "path": [ "..", + "... on T", "prop" ], "renameKeyTo": "contextualArgument_1_0" } ], - "schemaAwareHash": "c3941670991c332a40e1b0fc3b6d01a04211e2341f1a36a2c42bee4541d985e4", + "schemaAwareHash": "66b954f39aead8436321c671eb71e56ce15bbe0c7b82f06b2f8f70473ce1cb6e", "authorization": { "is_authenticated": false, "scopes": [], From 9e64881a875ded1d0a7dcfbddf778c91f1f1c67c Mon Sep 17 00:00:00 2001 From: Chris Lenfest Date: Wed, 15 May 2024 22:29:27 -0500 Subject: [PATCH 33/69] adding some comments --- apollo-router/src/query_planner/subgraph_context.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/apollo-router/src/query_planner/subgraph_context.rs b/apollo-router/src/query_planner/subgraph_context.rs index 71e119881e..4d9a2f2824 100644 --- a/apollo-router/src/query_planner/subgraph_context.rs +++ b/apollo-router/src/query_planner/subgraph_context.rs @@ -122,6 +122,9 @@ impl<'a> SubgraphContext<'a> { None } + // For each of the rewrites, start collecting data for the data at path. + // Once we find a Value for a given variable, skip additional rewrites that + // reference the same variable pub(crate) fn execute_on_path(&mut self, path: &Path) { let mut found_rewrites: HashSet = HashSet::new(); let hash_map: HashMap = self @@ -176,6 +179,9 @@ impl<'a> SubgraphContext<'a> { self.named_args.push(hash_map); } + // Once all a value has been extracted for every variable, go ahead and add all + // variables to the variables map. Additionally, return a ContextualArguments structure if + // values of variables are entity dependent pub(crate) fn add_variables_and_get_args( &self, variables: &mut Map, @@ -222,6 +228,8 @@ impl<'a> SubgraphContext<'a> { } } +// Take the existing subgraph operation and rewrite it to use aliasing. This will occur in the case +// where we are collecting entites and different entities may have different variables passed to the resolver. pub(crate) fn build_operation_with_aliasing( subgraph_operation: &SubgraphOperation, contextual_arguments: &Option, @@ -271,7 +279,7 @@ pub(crate) fn build_operation_with_aliasing( selections, }, }) - } else { + } else { // TODO: This is ugly, but I get compiler errors if the else blocks aren't present Err(ContextBatchingError::NoSelectionSet) } } else { From 49c5c822de78eb550cf31faba98f479005e5ae35 Mon Sep 17 00:00:00 2001 From: o0Ignition0o Date: Thu, 16 May 2024 13:03:48 +0200 Subject: [PATCH 34/69] update dependency --- Cargo.lock | 29 ++----------------- apollo-router/Cargo.toml | 2 +- .../fixtures/set_context/supergraph.graphql | 2 +- 3 files changed, 4 insertions(+), 29 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a35ceb3cdd..7a1a033f17 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -360,7 +360,7 @@ dependencies = [ "regex", "reqwest", "rhai", - "router-bridge 0.5.21+v2.7.5 (git+https://github.com/apollographql/federation-rs?rev=518ce143088f84575e2c64436985802a25458a13)", + "router-bridge", "rstack", "rust-embed", "rustls", @@ -5719,31 +5719,6 @@ dependencies = [ "paste", ] -[[package]] -name = "router-bridge" -version = "0.5.21+v2.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2142445fe3fe2aae7a3c3c5083d1211a448a0dabb489a14dd90d427cf6c0b13" -dependencies = [ - "anyhow", - "async-channel 1.9.0", - "deno_console", - "deno_core", - "deno_crypto", - "deno_url", - "deno_web", - "deno_webidl", - "rand 0.8.5", - "serde", - "serde_json", - "thiserror", - "tokio", - "tower", - "tower-service", - "tracing", - "which", -] - [[package]] name = "router-bridge" version = "0.5.21+v2.7.5" @@ -5783,7 +5758,7 @@ dependencies = [ "libfuzzer-sys", "log", "reqwest", - "router-bridge 0.5.21+v2.7.5 (registry+https://github.com/rust-lang/crates.io-index)", + "router-bridge", "schemars", "serde", "serde_json", diff --git a/apollo-router/Cargo.toml b/apollo-router/Cargo.toml index 786f5bce19..892fd7ba9e 100644 --- a/apollo-router/Cargo.toml +++ b/apollo-router/Cargo.toml @@ -185,7 +185,7 @@ regex = "1.10.3" reqwest.workspace = true # note: this dependency should _always_ be pinned, prefix the version with an `=` -router-bridge = { git = "https://github.com/apollographql/federation-rs", rev = "518ce143088f84575e2c64436985802a25458a13" } +router-bridge = { git = "https://github.com/apollographql/federation-rs", rev = "ced6eed5c4394bb3cbf3867798b8b7f63cb82d66" } rust-embed = "8.2.0" rustls = "0.21.11" diff --git a/apollo-router/tests/fixtures/set_context/supergraph.graphql b/apollo-router/tests/fixtures/set_context/supergraph.graphql index 87fe61a3d3..658e578aff 100644 --- a/apollo-router/tests/fixtures/set_context/supergraph.graphql +++ b/apollo-router/tests/fixtures/set_context/supergraph.graphql @@ -1,6 +1,6 @@ schema @link(url: "https://specs.apollo.dev/link/v1.0") - @link(url: "https://specs.apollo.dev/join/v0.4", for: EXECUTION) + @link(url: "https://specs.apollo.dev/join/v0.5", for: EXECUTION) @link(url: "https://specs.apollo.dev/context/v0.1", for: SECURITY) { query: Query } From 66992c2cf074d3d4b45d7498259cbb657c4ca65c Mon Sep 17 00:00:00 2001 From: o0Ignition0o Date: Thu, 16 May 2024 13:13:56 +0200 Subject: [PATCH 35/69] update dependency requirements across examples --- Cargo.lock | 668 ++++++++++++++++----------------------- apollo-router/Cargo.toml | 2 +- fuzz/Cargo.toml | 2 +- 3 files changed, 270 insertions(+), 402 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7a1a033f17..f86d403bb8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -48,14 +48,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" dependencies = [ "crypto-common", - "generic-array 0.14.7", + "generic-array", ] [[package]] name = "aes" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "433cfd6710c9986c576a25ca913c39d66a6474107b406f34f91d4a8923395241" +checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2" dependencies = [ "cfg-if", "cipher", @@ -295,7 +295,7 @@ dependencies = [ "diff", "directories", "displaydoc", - "ecdsa 0.16.9", + "ecdsa", "flate2", "fred", "futures", @@ -347,7 +347,7 @@ dependencies = [ "opentelemetry-zipkin", "opentelemetry_api", "opentelemetry_sdk 0.20.0", - "p256 0.13.2", + "p256", "parking_lot", "paste", "pin-project-lite", @@ -1058,7 +1058,7 @@ version = "1.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897f1db4020ad91f2c2421945ec49b7e3eb81cc3fea99e8b5dd5be721e697fed" dependencies = [ - "base64-simd", + "base64-simd 0.8.0", "bytes", "bytes-utils", "futures-core", @@ -1165,12 +1165,6 @@ dependencies = [ "rustc-demangle", ] -[[package]] -name = "base16ct" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" - [[package]] name = "base16ct" version = "0.2.0" @@ -1189,13 +1183,22 @@ version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" +[[package]] +name = "base64-simd" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "781dd20c3aff0bd194fe7d2a977dd92f21c173891f3a03b677359e5fa457e5d5" +dependencies = [ + "simd-abstraction", +] + [[package]] name = "base64-simd" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "339abbe78e73178762e23bea9dfd08e697eb3f3301cd4be981c0f78ba5859195" dependencies = [ - "outref", + "outref 0.5.1", "vsimd", ] @@ -1214,6 +1217,15 @@ dependencies = [ "serde", ] +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + [[package]] name = "bit-set" version = "0.5.3" @@ -1241,13 +1253,25 @@ version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + [[package]] name = "block-buffer" version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ - "generic-array 0.14.7", + "generic-array", ] [[package]] @@ -1256,7 +1280,7 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" dependencies = [ - "generic-array 0.14.7", + "generic-array", ] [[package]] @@ -1352,9 +1376,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "bytes" -version = "1.5.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" [[package]] name = "bytes-utils" @@ -1528,15 +1552,6 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" -[[package]] -name = "cmake" -version = "0.1.50" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31c789563b815f77f4250caee12365734369f942439b7defd71e18a48197130" -dependencies = [ - "cc", -] - [[package]] name = "colorchoice" version = "1.0.0" @@ -1679,6 +1694,12 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" +[[package]] +name = "cooked-waker" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147be55d677052dabc6b22252d5dd0fd4c29c8c27aa4f2fbef0f94aa003b406f" + [[package]] name = "cookie" version = "0.18.0" @@ -1849,25 +1870,13 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" -[[package]] -name = "crypto-bigint" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" -dependencies = [ - "generic-array 0.14.7", - "rand_core 0.6.4", - "subtle", - "zeroize", -] - [[package]] name = "crypto-bigint" version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" dependencies = [ - "generic-array 0.14.7", + "generic-array", "rand_core 0.6.4", "subtle", "zeroize", @@ -1879,7 +1888,7 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ - "generic-array 0.14.7", + "generic-array", "rand_core 0.6.4", "typenum", ] @@ -1905,22 +1914,9 @@ dependencies = [ [[package]] name = "curve25519-dalek" -version = "2.1.3" +version = "4.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a9b85542f99a2dfa2a1b8e192662741c9859a846b296bef1c92ef9b58b5a216" -dependencies = [ - "byteorder", - "digest 0.8.1", - "rand_core 0.5.1", - "subtle", - "zeroize", -] - -[[package]] -name = "curve25519-dalek" -version = "4.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f711ade317dd348950a9910f81c5947e3d8907ebd2b83f76203ff1807e6a2bc2" +checksum = "0a677b8922c94e01bdbb12126b0bc852f00447528dee1782229af9c720c3f348" dependencies = [ "cfg-if", "cpufeatures", @@ -2002,52 +1998,33 @@ dependencies = [ "uuid", ] -[[package]] -name = "deno-proc-macro-rules" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c65c2ffdafc1564565200967edc4851c7b55422d3913466688907efd05ea26f" -dependencies = [ - "deno-proc-macro-rules-macros", - "proc-macro2 1.0.76", - "syn 2.0.48", -] - -[[package]] -name = "deno-proc-macro-rules-macros" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3047b312b7451e3190865713a4dd6e1f821aed614ada219766ebc3024a690435" -dependencies = [ - "once_cell", - "proc-macro2 1.0.76", - "quote 1.0.35", - "syn 2.0.48", -] - [[package]] name = "deno_console" -version = "0.115.0" +version = "0.151.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19ab05b798826985966deb29fc6773ed29570de2f2147a30c4289c7cdf635214" +checksum = "b1657ea36f527a5fa3b3d9c1ad9b9f0c9c0b263f966079f5aa0c7c09ff6f4e2e" dependencies = [ "deno_core", ] [[package]] name = "deno_core" -version = "0.200.0" +version = "0.280.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8ba264b90ceb6e95b39d82e674d8ecae86ca012f900338ea50d1a077d9d75fd" +checksum = "12d26f2d3e243bbbdd0851ab542b20ec48ac1fcf6c64ab06e81133da3113ebdd" dependencies = [ "anyhow", + "bincode", + "bit-set", + "bit-vec", "bytes", + "cooked-waker", + "deno_core_icudata", "deno_ops", + "deno_unsync", "futures", - "indexmap 1.9.3", "libc", - "log", - "once_cell", + "memoffset 0.9.0", "parking_lot", "pin-project", "serde", @@ -2055,42 +2032,49 @@ dependencies = [ "serde_v8", "smallvec", "sourcemap", + "static_assertions", "tokio", "url", "v8", ] +[[package]] +name = "deno_core_icudata" +version = "0.0.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a13951ea98c0a4c372f162d669193b4c9d991512de9f2381dd161027f34b26b1" + [[package]] name = "deno_crypto" -version = "0.129.0" +version = "0.165.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7247d39660238354f71103f0db7a944c3ac3fb6759ab5410d708f7496eaa89a5" +checksum = "f399d4bd84b014f5dcf9f0ceda39d7fc5624c732fb2c71f93dea4af2b5daa706" dependencies = [ "aes", "aes-gcm", "aes-kw", - "base64 0.13.1", + "base64 0.21.7", "cbc", "const-oid", "ctr", - "curve25519-dalek 2.1.3", + "curve25519-dalek", "deno_core", "deno_web", - "elliptic-curve 0.12.3", + "elliptic-curve", "num-traits", "once_cell", - "p256 0.11.1", + "p256", "p384", + "p521", "rand 0.8.5", - "ring 0.16.20", + "ring 0.17.5", "rsa", - "sec1 0.3.0", "serde", "serde_bytes", "sha1", "sha2", - "signature 1.6.4", - "spki 0.6.0", + "signature", + "spki", "tokio", "uuid", "x25519-dalek", @@ -2098,30 +2082,33 @@ dependencies = [ [[package]] name = "deno_ops" -version = "0.78.0" +version = "0.156.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffd1c83b1fd465ee0156f2917c9af9ca09fe2bf54052a2cae1a8dcbc7b89aefc" +checksum = "8237b272db1a6cb941b8a5a63ba63539004a8263e8b0230a11136d76eea273f9" dependencies = [ - "deno-proc-macro-rules", - "lazy-regex", - "once_cell", - "pmutil", - "proc-macro-crate", + "proc-macro-rules", "proc-macro2 1.0.76", "quote 1.0.35", - "regex", "strum 0.25.0", "strum_macros 0.25.3", - "syn 1.0.109", "syn 2.0.48", "thiserror", ] +[[package]] +name = "deno_unsync" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d79c7af81e0a5ac75cff7b2fff4d1896e2bff694c688258edf21ef8a519736" +dependencies = [ + "tokio", +] + [[package]] name = "deno_url" -version = "0.115.0" +version = "0.151.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20490fff3b0f8c176a815e26371ff23313ea7f39cd51057701524c5b6fc36f6c" +checksum = "c6babc9a52e441a08f9c4f03309fe80f1f7294a4c227bbddf064b3e1c62abbf0" dependencies = [ "deno_core", "serde", @@ -2130,15 +2117,17 @@ dependencies = [ [[package]] name = "deno_web" -version = "0.146.0" +version = "0.182.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dc8dda6e1337d4739ae9e94d75521689824d82a7deb154a2972b6eedac64507" +checksum = "86ce3a0a97a69379188214d92e03c22437f96f068d37686d1cd1eff34e4517a8" dependencies = [ "async-trait", - "base64-simd", + "base64-simd 0.8.0", + "bytes", "deno_core", "encoding_rs", "flate2", + "futures", "serde", "tokio", "uuid", @@ -2147,24 +2136,13 @@ dependencies = [ [[package]] name = "deno_webidl" -version = "0.115.0" +version = "0.151.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73159d81053ead02e938b46d4bb7224c8e7cf25273ac16a250fb45bb09af7635" +checksum = "4ffa30c6acba42d8a4d952514bbb7b5d247b9c900fa1fd82ce9084c32f640827" dependencies = [ "deno_core", ] -[[package]] -name = "der" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" -dependencies = [ - "const-oid", - "pem-rfc7468 0.6.0", - "zeroize", -] - [[package]] name = "der" version = "0.7.8" @@ -2172,7 +2150,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" dependencies = [ "const-oid", - "pem-rfc7468 0.7.0", + "pem-rfc7468", "zeroize", ] @@ -2262,15 +2240,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" -[[package]] -name = "digest" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" -dependencies = [ - "generic-array 0.12.4", -] - [[package]] name = "digest" version = "0.10.7" @@ -2363,30 +2332,18 @@ version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbfc4744c1b8f2a09adc0e55242f60b1af195d88596bd8700be74418c056c555" -[[package]] -name = "ecdsa" -version = "0.14.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" -dependencies = [ - "der 0.6.1", - "elliptic-curve 0.12.3", - "rfc6979 0.3.1", - "signature 1.6.4", -] - [[package]] name = "ecdsa" version = "0.16.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" dependencies = [ - "der 0.7.8", - "digest 0.10.7", - "elliptic-curve 0.13.8", - "rfc6979 0.4.0", - "signature 2.0.0", - "spki 0.7.2", + "der", + "digest", + "elliptic-curve", + "rfc6979", + "signature", + "spki", ] [[package]] @@ -2395,44 +2352,23 @@ version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" -[[package]] -name = "elliptic-curve" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" -dependencies = [ - "base16ct 0.1.1", - "crypto-bigint 0.4.9", - "der 0.6.1", - "digest 0.10.7", - "ff 0.12.1", - "generic-array 0.14.7", - "group 0.12.1", - "hkdf", - "pem-rfc7468 0.6.0", - "pkcs8 0.9.0", - "rand_core 0.6.4", - "sec1 0.3.0", - "subtle", - "zeroize", -] - [[package]] name = "elliptic-curve" version = "0.13.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" dependencies = [ - "base16ct 0.2.0", - "crypto-bigint 0.5.5", - "digest 0.10.7", - "ff 0.13.0", - "generic-array 0.14.7", - "group 0.13.0", - "pem-rfc7468 0.7.0", - "pkcs8 0.10.2", + "base16ct", + "crypto-bigint", + "digest", + "ff", + "generic-array", + "group", + "hkdf", + "pem-rfc7468", + "pkcs8", "rand_core 0.6.4", - "sec1 0.7.3", + "sec1", "subtle", "zeroize", ] @@ -2445,9 +2381,9 @@ checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" [[package]] name = "encoding_rs" -version = "0.8.31" +version = "0.8.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b" +checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" dependencies = [ "cfg-if", ] @@ -2594,16 +2530,6 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" -[[package]] -name = "ff" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" -dependencies = [ - "rand_core 0.6.4", - "subtle", -] - [[package]] name = "ff" version = "0.13.0" @@ -2616,9 +2542,9 @@ dependencies = [ [[package]] name = "fiat-crypto" -version = "0.1.20" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e825f6987101665dea6ec934c09ec6d721de7bc1bf92248e1d5810c8cd636b77" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" [[package]] name = "filetime" @@ -2645,7 +2571,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" dependencies = [ "crc32fast", - "libz-ng-sys", "miniz_oxide", ] @@ -2783,14 +2708,20 @@ dependencies = [ [[package]] name = "fslock" -version = "0.1.8" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57eafdd0c16f57161105ae1b98a1238f97645f2f588438b2949c99a2af9616bf" +checksum = "04412b8935272e3a9bae6f48c7bfff74c2911f60525404edfdd28e49884c3bfb" dependencies = [ "libc", "winapi", ] +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + [[package]] name = "futures" version = "0.3.30" @@ -2934,15 +2865,6 @@ dependencies = [ "slab", ] -[[package]] -name = "generic-array" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd" -dependencies = [ - "typenum", -] - [[package]] name = "generic-array" version = "0.14.7" @@ -3111,24 +3033,22 @@ dependencies = [ [[package]] name = "group" -version = "0.12.1" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" dependencies = [ - "ff 0.12.1", + "ff", "rand_core 0.6.4", "subtle", ] [[package]] -name = "group" -version = "0.13.0" +name = "gzip-header" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +checksum = "95cc527b92e6029a62960ad99aa8a6660faa4555fe5f731aab13aa6a921795a2" dependencies = [ - "ff 0.13.0", - "rand_core 0.6.4", - "subtle", + "crc32fast", ] [[package]] @@ -3275,7 +3195,7 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "digest 0.10.7", + "digest", ] [[package]] @@ -3422,7 +3342,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2 0.4.9", + "socket2 0.5.5", "tokio", "tower-service", "tracing", @@ -3563,7 +3483,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" dependencies = [ "block-padding", - "generic-array 0.14.7", + "generic-array", ] [[package]] @@ -3814,29 +3734,6 @@ dependencies = [ "log", ] -[[package]] -name = "lazy-regex" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff63c423c68ea6814b7da9e88ce585f793c87ddd9e78f646970891769c8235d4" -dependencies = [ - "lazy-regex-proc_macros", - "once_cell", - "regex", -] - -[[package]] -name = "lazy-regex-proc_macros" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8edfc11b8f56ce85e207e62ea21557cfa09bb24a8f6b04ae181b086ff8611c22" -dependencies = [ - "proc-macro2 1.0.76", - "quote 1.0.35", - "regex", - "syn 1.0.109", -] - [[package]] name = "lazy_static" version = "1.4.0" @@ -3897,16 +3794,6 @@ dependencies = [ "vcpkg", ] -[[package]] -name = "libz-ng-sys" -version = "1.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dd9f43e75536a46ee0f92b758f6b63846e594e86638c61a9251338a65baea63" -dependencies = [ - "cmake", - "libc", -] - [[package]] name = "libz-sys" version = "1.1.12" @@ -4095,9 +3982,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" dependencies = [ "adler", ] @@ -4727,6 +4614,12 @@ dependencies = [ "num-traits", ] +[[package]] +name = "outref" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f222829ae9293e33a9f5e9f440c6760a3d450a64affe1846486b140db81c1f4" + [[package]] name = "outref" version = "0.5.1" @@ -4741,35 +4634,39 @@ checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" [[package]] name = "p256" -version = "0.11.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51f44edd08f51e2ade572f141051021c5af22677e42b7dd28a88155151c33594" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" dependencies = [ - "ecdsa 0.14.8", - "elliptic-curve 0.12.3", + "ecdsa", + "elliptic-curve", + "primeorder", "sha2", ] [[package]] -name = "p256" -version = "0.13.2" +name = "p384" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +checksum = "70786f51bcc69f6a4c0360e063a4cac5419ef7c5cd5b3c99ad70f3be5ba79209" dependencies = [ - "ecdsa 0.16.9", - "elliptic-curve 0.13.8", + "ecdsa", + "elliptic-curve", "primeorder", "sha2", ] [[package]] -name = "p384" -version = "0.11.2" +name = "p521" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc8c5bf642dde52bb9e87c0ecd8ca5a76faac2eeed98dedb7c717997e1080aa" +checksum = "0fc9e2161f1f215afdfce23677034ae137bbd45016a880c2eb3ba8eb95f085b2" dependencies = [ - "ecdsa 0.14.8", - "elliptic-curve 0.12.3", + "base16ct", + "ecdsa", + "elliptic-curve", + "primeorder", + "rand_core 0.6.4", "sha2", ] @@ -4818,15 +4715,6 @@ dependencies = [ "serde", ] -[[package]] -name = "pem-rfc7468" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d159833a9105500e0398934e205e0773f0b27529557134ecfc51c27646adac" -dependencies = [ - "base64ct", -] - [[package]] name = "pem-rfc7468" version = "0.7.0" @@ -4943,24 +4831,13 @@ dependencies = [ [[package]] name = "pkcs1" -version = "0.4.1" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eff33bdbdfc54cc98a2eca766ebdec3e1b8fb7387523d5c9c9a2891da856f719" +checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" dependencies = [ - "der 0.6.1", - "pkcs8 0.9.0", - "spki 0.6.0", - "zeroize", -] - -[[package]] -name = "pkcs8" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" -dependencies = [ - "der 0.6.1", - "spki 0.6.0", + "der", + "pkcs8", + "spki", ] [[package]] @@ -4969,8 +4846,8 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" dependencies = [ - "der 0.7.8", - "spki 0.7.2", + "der", + "spki", ] [[package]] @@ -5013,17 +4890,6 @@ dependencies = [ "plotters-backend", ] -[[package]] -name = "pmutil" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52a40bc70c2c58040d2d8b167ba9a5ff59fc9dab7ad44771cfde3dcfde7a09c6" -dependencies = [ - "proc-macro2 1.0.76", - "quote 1.0.35", - "syn 2.0.48", -] - [[package]] name = "polling" version = "2.8.0" @@ -5130,17 +4996,30 @@ version = "0.13.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" dependencies = [ - "elliptic-curve 0.13.8", + "elliptic-curve", ] [[package]] -name = "proc-macro-crate" -version = "1.3.1" +name = "proc-macro-rules" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07c277e4e643ef00c1233393c673f655e3672cf7eb3ba08a00bdd0ea59139b5f" +dependencies = [ + "proc-macro-rules-macros", + "proc-macro2 1.0.76", + "syn 2.0.48", +] + +[[package]] +name = "proc-macro-rules-macros" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +checksum = "207fffb0fe655d1d47f6af98cc2793405e85929bdbc420d685554ff07be27ac7" dependencies = [ "once_cell", - "toml_edit 0.19.14", + "proc-macro2 1.0.76", + "quote 1.0.35", + "syn 2.0.48", ] [[package]] @@ -5230,7 +5109,7 @@ dependencies = [ "regex", "syn 1.0.109", "tempfile", - "which", + "which 4.4.2", ] [[package]] @@ -5321,6 +5200,12 @@ dependencies = [ "proc-macro2 1.0.76", ] +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + [[package]] name = "rand" version = "0.7.3" @@ -5569,17 +5454,6 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4389f1d5789befaf6029ebd9f7dac4af7f7e3d61b69d4f30e2ac02b57e7712b0" -[[package]] -name = "rfc6979" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" -dependencies = [ - "crypto-bigint 0.4.9", - "hmac", - "zeroize", -] - [[package]] name = "rfc6979" version = "0.4.0" @@ -5722,7 +5596,7 @@ dependencies = [ [[package]] name = "router-bridge" version = "0.5.21+v2.7.5" -source = "git+https://github.com/apollographql/federation-rs?rev=518ce143088f84575e2c64436985802a25458a13#518ce143088f84575e2c64436985802a25458a13" +source = "git+https://github.com/apollographql/federation-rs?rev=ced6eed5c4394bb3cbf3867798b8b7f63cb82d66#ced6eed5c4394bb3cbf3867798b8b7f63cb82d66" dependencies = [ "anyhow", "async-channel 1.9.0", @@ -5740,7 +5614,7 @@ dependencies = [ "tower", "tower-service", "tracing", - "which", + "which 4.4.2", ] [[package]] @@ -5782,21 +5656,20 @@ dependencies = [ [[package]] name = "rsa" -version = "0.7.2" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "094052d5470cbcef561cb848a7209968c9f12dfa6d668f4bca048ac5de51099c" +checksum = "af6c4b23d99685a1408194da11270ef8e9809aff951cc70ec9b17350b087e474" dependencies = [ - "byteorder", - "digest 0.10.7", + "const-oid", + "digest", "num-bigint-dig", "num-integer", - "num-iter", "num-traits", "pkcs1", - "pkcs8 0.9.0", + "pkcs8", "rand_core 0.6.4", - "signature 1.6.4", - "smallvec", + "signature", + "spki", "subtle", "zeroize", ] @@ -6019,30 +5892,16 @@ dependencies = [ "untrusted 0.7.1", ] -[[package]] -name = "sec1" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" -dependencies = [ - "base16ct 0.1.1", - "der 0.6.1", - "generic-array 0.14.7", - "pkcs8 0.9.0", - "subtle", - "zeroize", -] - [[package]] name = "sec1" version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" dependencies = [ - "base16ct 0.2.0", - "der 0.7.8", - "generic-array 0.14.7", - "pkcs8 0.10.2", + "base16ct", + "der", + "generic-array", + "pkcs8", "subtle", "zeroize", ] @@ -6211,15 +6070,12 @@ dependencies = [ [[package]] name = "serde_v8" -version = "0.111.0" +version = "0.189.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "309b3060a9627882514f3a3ce3cc08ceb347a76aeeadc58f138c3f189cf88b71" +checksum = "893c995255d6fbf55c33166b651fd037c4e3cc7864bf82213ea18d0ec94ed165" dependencies = [ - "bytes", - "derive_more", "num-bigint", "serde", - "serde_bytes", "smallvec", "thiserror", "v8", @@ -6270,7 +6126,7 @@ checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ "cfg-if", "cpufeatures", - "digest 0.10.7", + "digest", ] [[package]] @@ -6281,7 +6137,7 @@ checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" dependencies = [ "cfg-if", "cpufeatures", - "digest 0.10.7", + "digest", ] [[package]] @@ -6319,22 +6175,21 @@ dependencies = [ [[package]] name = "signature" -version = "1.6.4" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" dependencies = [ - "digest 0.10.7", + "digest", "rand_core 0.6.4", ] [[package]] -name = "signature" -version = "2.0.0" +name = "simd-abstraction" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fe458c98333f9c8152221191a77e2a44e8325d0193484af2e9421a53019e57d" +checksum = "9cadb29c57caadc51ff8346233b5cec1d240b68ce55cf1afc764818791876987" dependencies = [ - "digest 0.10.7", - "rand_core 0.6.4", + "outref 0.1.0", ] [[package]] @@ -6413,17 +6268,20 @@ dependencies = [ [[package]] name = "sourcemap" -version = "6.4.1" +version = "8.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4cbf65ca7dc576cf50e21f8d0712d96d4fcfd797389744b7b222a85cdf5bd90" +checksum = "208d40b9e8cad9f93613778ea295ed8f3c2b1824217c6cfc7219d3f6f45b96d4" dependencies = [ + "base64-simd 0.7.0", + "bitvec", "data-encoding", "debugid", "if_chain", + "rustc-hash", "rustc_version 0.2.3", "serde", "serde_json", - "unicode-id", + "unicode-id-start", "url", ] @@ -6439,16 +6297,6 @@ version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" -[[package]] -name = "spki" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" -dependencies = [ - "base64ct", - "der 0.6.1", -] - [[package]] name = "spki" version = "0.7.2" @@ -6456,7 +6304,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d1e996ef02c474957d681f1b05213dfb0abab947b446a62d37770b23500184a" dependencies = [ "base64ct", - "der 0.7.8", + "der", ] [[package]] @@ -6622,6 +6470,12 @@ dependencies = [ "libc", ] +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + [[package]] name = "tempfile" version = "3.10.0" @@ -7000,7 +6854,7 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.22.4", + "toml_edit", ] [[package]] @@ -7012,17 +6866,6 @@ dependencies = [ "serde", ] -[[package]] -name = "toml_edit" -version = "0.19.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8123f27e969974a3dfba720fdb560be359f57b44302d280ba72e76a74480e8a" -dependencies = [ - "indexmap 2.2.3", - "toml_datetime", - "winnow", -] - [[package]] name = "toml_edit" version = "0.22.4" @@ -7552,10 +7395,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] -name = "unicode-id" -version = "0.3.3" +name = "unicode-id-start" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d70b6494226b36008c8366c288d77190b3fad2eb4c10533139c1c1f461127f1a" +checksum = "b8f73150333cb58412db36f2aca8f2875b013049705cc77b94ded70a1ab1f5da" [[package]] name = "unicode-ident" @@ -7671,14 +7514,17 @@ dependencies = [ [[package]] name = "v8" -version = "0.74.3" +version = "0.91.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eedac634b8dd39b889c5b62349cbc55913780226239166435c5cf66771792ea" +checksum = "69026e2e8af55a4d2f20c0c17f690e8b31472bf76ab75b1205d3a0fab60c8f84" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.4.0", "fslock", + "gzip-header", + "home", + "miniz_oxide", "once_cell", - "which", + "which 5.0.0", ] [[package]] @@ -7861,6 +7707,19 @@ dependencies = [ "rustix 0.38.31", ] +[[package]] +name = "which" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bf3ea8596f3a0dd5980b46430f2058dfe2c36a27ccfbb1845d6fbfcd9ba6e14" +dependencies = [ + "either", + "home", + "once_cell", + "rustix 0.38.31", + "windows-sys 0.48.0", +] + [[package]] name = "widestring" version = "1.0.2" @@ -8143,13 +8002,22 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8dab7ac864710bdea6594becbea5b5050333cf34fefb0dc319567eb347950d4" +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + [[package]] name = "x25519-dalek" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb66477291e7e8d2b0ff1bcb900bf29489a9692816d79874bea351e7a8b6de96" dependencies = [ - "curve25519-dalek 4.0.0", + "curve25519-dalek", "rand_core 0.6.4", "serde", "zeroize", diff --git a/apollo-router/Cargo.toml b/apollo-router/Cargo.toml index 892fd7ba9e..0bbc381e9e 100644 --- a/apollo-router/Cargo.toml +++ b/apollo-router/Cargo.toml @@ -79,7 +79,7 @@ axum = { version = "0.6.20", features = ["headers", "json", "original-uri"] } base64 = "0.21.7" bloomfilter = "1.0.13" buildstructor = "0.5.4" -bytes = "1.5.0" +bytes = "1.6.0" clap = { version = "4.5.1", default-features = false, features = [ "env", "derive", diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index ace1dead0a..d36d923c6d 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -20,7 +20,7 @@ reqwest = { workspace = true, features = ["json", "blocking"] } serde_json.workspace = true tokio.workspace = true # note: this dependency should _always_ be pinned, prefix the version with an `=` -router-bridge = { git = "https://github.com/apollographql/federation-rs", rev = "518ce143088f84575e2c64436985802a25458a13" } +router-bridge = { git = "https://github.com/apollographql/federation-rs", rev = "ced6eed5c4394bb3cbf3867798b8b7f63cb82d66" } [dev-dependencies] anyhow = "1" From a50e786691db72d75b326de21859e9ebf1b5f375 Mon Sep 17 00:00:00 2001 From: o0Ignition0o Date: Thu, 16 May 2024 15:01:22 +0200 Subject: [PATCH 36/69] wip --- .../src/link/join_spec_definition.rs | 4 + apollo-router/src/query_planner/fetch.rs | 2 +- .../tests/fixtures/set_context/one.json | 13 +- .../fixtures/set_context/supergraph.graphql | 20 ++- apollo-router/tests/set_context.rs | 137 ++++++++++-------- .../set_context__set_context_list.snap | 5 +- .../set_context__set_context_list.snap.new | 137 ------------------ 7 files changed, 102 insertions(+), 216 deletions(-) delete mode 100644 apollo-router/tests/snapshots/set_context__set_context_list.snap.new diff --git a/apollo-federation/src/link/join_spec_definition.rs b/apollo-federation/src/link/join_spec_definition.rs index c2b011e244..bf8734a4e6 100644 --- a/apollo-federation/src/link/join_spec_definition.rs +++ b/apollo-federation/src/link/join_spec_definition.rs @@ -336,6 +336,10 @@ lazy_static! { Version { major: 0, minor: 3 }, Some(Version { major: 2, minor: 0 }), )); + definitions.add(JoinSpecDefinition::new( + Version { major: 0, minor: 5 }, + Some(Version { major: 2, minor: 8 }), + )); definitions }; diff --git a/apollo-router/src/query_planner/fetch.rs b/apollo-router/src/query_planner/fetch.rs index a50fe41168..a6cdc4214f 100644 --- a/apollo-router/src/query_planner/fetch.rs +++ b/apollo-router/src/query_planner/fetch.rs @@ -397,7 +397,7 @@ impl FetchNode { let aliased_operation = build_operation_with_aliasing(operation, &contextual_arguments, parameters.schema); let alias_query_string; // this exists outside the if block to allow the as_str() to be longer lived let query_str = if let Ok(op) = aliased_operation { - alias_query_string = op.to_string(); + alias_query_string = op.serialize().no_indent().to_string(); alias_query_string.as_str() } else { operation.as_serialized() diff --git a/apollo-router/tests/fixtures/set_context/one.json b/apollo-router/tests/fixtures/set_context/one.json index 79271c87ec..560c60491a 100644 --- a/apollo-router/tests/fixtures/set_context/one.json +++ b/apollo-router/tests/fixtures/set_context/one.json @@ -140,7 +140,7 @@ }, { "request": { - "query": "query Query__Subgraph1__1($representations:[_Any!]!$contextualArgument_1_0:String!){_entities(representations:$representations){...on U{field(a:$contextualArgument_1_0)}}}", + "query": "query Query__Subgraph1__1($representations:[_Any!]!$contextualArgument_1_0:String){_entities(representations:$representations){...on U{field(a:$contextualArgument_1_0)}}}", "operationName": "Query__Subgraph1__1", "variables": { "contextualArgument_1_0": "prop value", @@ -161,7 +161,7 @@ }, { "request": { - "query": "query Query__Subgraph1__1($representations:[_Any!]!$contextualArgument_1_0:String!){_entities(representations:$representations){...on U{field(a:$contextualArgument_1_0)}}}", + "query": "query Query__Subgraph1__1($representations:[_Any!]!$contextualArgument_1_0:String){_entities(representations:$representations){...on U{field(a:$contextualArgument_1_0)}}}", "operationName": "Query__Subgraph1__1", "variables": { "contextualArgument_1_0": "prop value", @@ -249,13 +249,11 @@ }, { "request": { - "query": "query QueryUnion__Subgraph1__1($representations:[_Any!]!$contextualArgument_1_1:String!){_entities(representations:$representations){...on V{field(a:$contextualArgument_1_1)}}}", + "query": "query QueryUnion__Subgraph1__1($representations:[_Any!]!$contextualArgument_1_1:String){_entities(representations:$representations){...on V{field(a:$contextualArgument_1_1)}}}", "operationName": "QueryUnion__Subgraph1__1", "variables": { "contextualArgument_1_1": "prop value 3", - "representations": [ - { "__typename": "V", "id": "2" } - ] + "representations": [{ "__typename": "V", "id": "2" }] } }, "response": { @@ -268,5 +266,6 @@ ] } } - } ] + } + ] } diff --git a/apollo-router/tests/fixtures/set_context/supergraph.graphql b/apollo-router/tests/fixtures/set_context/supergraph.graphql index 658e578aff..b2bab6b310 100644 --- a/apollo-router/tests/fixtures/set_context/supergraph.graphql +++ b/apollo-router/tests/fixtures/set_context/supergraph.graphql @@ -5,7 +5,7 @@ schema query: Query } -directive @context(name: String!) repeatable on INTERFACE | OBJECT | UNION +directive @context(name: String!) repeatable on INTERFACE | OBJECT directive @context__fromContext(field: String) on ARGUMENT_DEFINITION @@ -94,19 +94,23 @@ type Query @join__type(graph: SUBGRAPH1) @join__type(graph: SUBGRAPH2) { t: T! @join__field(graph: SUBGRAPH1) tList: [T]! @join__field(graph: SUBGRAPH1) a: Int! @join__field(graph: SUBGRAPH2) + k: K! @join__field(graph: SUBGRAPH1) } -type A - @join__type(graph: SUBGRAPH1, key: "id") -{ +union K + @join__type(graph: SUBGRAPH1) + @join__unionMember(graph: SUBGRAPH1, member: "A") + @join__unionMember(graph: SUBGRAPH1, member: "B") = + A + | B + +type A @join__type(graph: SUBGRAPH1, key: "id") { id: ID! v: V! prop: String! } -type B - @join__type(graph: SUBGRAPH1, key: "id") -{ +type B @join__type(graph: SUBGRAPH1, key: "id") { id: ID! v: V! prop: String! @@ -138,7 +142,7 @@ type U } ] ) - } +} type V @join__type(graph: SUBGRAPH1, key: "id") diff --git a/apollo-router/tests/set_context.rs b/apollo-router/tests/set_context.rs index d6387a95a8..c2bf12c142 100644 --- a/apollo-router/tests/set_context.rs +++ b/apollo-router/tests/set_context.rs @@ -23,10 +23,7 @@ struct RequestAndResponse { response: Response, } -async fn run_single_request( - query: &str, - mocks: &[(&'static str, &'static str)], -) -> Response { +async fn run_single_request(query: &str, mocks: &[(&'static str, &'static str)]) -> Response { let harness = setup_from_mocks( json! {{ "experimental_type_conditioned_fetching": true, @@ -59,44 +56,58 @@ async fn run_single_request( #[tokio::test(flavor = "multi_thread")] async fn test_set_context() { - let response = run_single_request(QUERY, &[ - ("Subgraph1", include_str!("fixtures/set_context/one.json")), - ("Subgraph2", include_str!("fixtures/set_context/two.json")), - ]).await; - + let response = run_single_request( + QUERY, + &[ + ("Subgraph1", include_str!("fixtures/set_context/one.json")), + ("Subgraph2", include_str!("fixtures/set_context/two.json")), + ], + ) + .await; + insta::assert_json_snapshot!(response); } #[tokio::test(flavor = "multi_thread")] async fn test_set_context_no_typenames() { - let response = run_single_request(QUERY_NO_TYPENAMES, &[ - ("Subgraph1", include_str!("fixtures/set_context/one.json")), - ("Subgraph2", include_str!("fixtures/set_context/two.json")), - ]).await; - - insta::assert_json_snapshot!(response); + let response = run_single_request( + QUERY_NO_TYPENAMES, + &[ + ("Subgraph1", include_str!("fixtures/set_context/one.json")), + ("Subgraph2", include_str!("fixtures/set_context/two.json")), + ], + ) + .await; + insta::assert_json_snapshot!(response); } #[tokio::test(flavor = "multi_thread")] async fn test_set_context_list() { - let response = run_single_request(QUERY_WITH_LIST, &[ - ("Subgraph1", include_str!("fixtures/set_context/one.json")), - ("Subgraph2", include_str!("fixtures/set_context/two.json")), - ]).await; - + let response = run_single_request( + QUERY_WITH_LIST, + &[ + ("Subgraph1", include_str!("fixtures/set_context/one.json")), + ("Subgraph2", include_str!("fixtures/set_context/two.json")), + ], + ) + .await; + insta::assert_json_snapshot!(response); } #[tokio::test(flavor = "multi_thread")] async fn test_set_context_list_of_lists() { - let response = run_single_request(QUERY_WITH_LIST_OF_LISTS, &[ - ("Subgraph1", include_str!("fixtures/set_context/one.json")), - ("Subgraph2", include_str!("fixtures/set_context/two.json")), - ]).await; - - insta::assert_json_snapshot!(response); + let response = run_single_request( + QUERY_WITH_LIST_OF_LISTS, + &[ + ("Subgraph1", include_str!("fixtures/set_context/one.json")), + ("Subgraph2", include_str!("fixtures/set_context/two.json")), + ], + ) + .await; + insta::assert_json_snapshot!(response); } #[tokio::test(flavor = "multi_thread")] @@ -166,55 +177,59 @@ fn setup_from_mocks( .extra_plugin(mocked_subgraphs) } -// TODO[clenfest]: figure out why i need __typename here? -static QUERY: &str = r#"query Query { - t { - __typename - id - u { +static QUERY: &str = r#" +query Query { + t { + __typename + id + u { __typename field - } } - }"#; -static QUERY_NO_TYPENAMES: &str = r#"query Query { - t { - id - u { + } +}"#; +static QUERY_NO_TYPENAMES: &str = r#" +query Query { + t { + id + u { field - } } - }"#; + } +}"#; -static QUERY_WITH_LIST: &str = r#"query Query { - t { - id - uList { +static QUERY_WITH_LIST: &str = r#" +query Query { + t { + id + uList { field - } } - }"#; + } +}"#; -static QUERY_WITH_LIST_OF_LISTS: &str = r#"query QueryLL { - tList { - id - uList { +static QUERY_WITH_LIST_OF_LISTS: &str = r#" +query QueryLL { + tList { + id + uList { field - } } - }"#; + } +}"#; -static QUERY_WITH_UNION: &str = r#"query QueryUnion { +static QUERY_WITH_UNION: &str = r#" +query QueryUnion { k { ... on A { - v { - field - } + v { + field + } } ... on B { - v { - field - } + v { + field + } } - } - }"#; + } +}"#; diff --git a/apollo-router/tests/snapshots/set_context__set_context_list.snap b/apollo-router/tests/snapshots/set_context__set_context_list.snap index bba9eb03c3..2a89743739 100644 --- a/apollo-router/tests/snapshots/set_context__set_context_list.snap +++ b/apollo-router/tests/snapshots/set_context__set_context_list.snap @@ -74,7 +74,7 @@ expression: response "variableUsages": [ "contextualArgument_1_0" ], - "operation": "query Query__Subgraph1__1($representations:[_Any!]!$contextualArgument_1_0:String!){_entities(representations:$representations){...on U{field(a:$contextualArgument_1_0)}}}", + "operation": "query Query__Subgraph1__1($representations:[_Any!]!$contextualArgument_1_0:String){_entities(representations:$representations){...on U{field(a:$contextualArgument_1_0)}}}", "operationName": "Query__Subgraph1__1", "operationKind": "query", "id": null, @@ -85,12 +85,13 @@ expression: response "kind": "KeyRenamer", "path": [ "..", + "... on T", "prop" ], "renameKeyTo": "contextualArgument_1_0" } ], - "schemaAwareHash": "c3941670991c332a40e1b0fc3b6d01a04211e2341f1a36a2c42bee4541d985e4", + "schemaAwareHash": "66b954f39aead8436321c671eb71e56ce15bbe0c7b82f06b2f8f70473ce1cb6e", "authorization": { "is_authenticated": false, "scopes": [], diff --git a/apollo-router/tests/snapshots/set_context__set_context_list.snap.new b/apollo-router/tests/snapshots/set_context__set_context_list.snap.new deleted file mode 100644 index 65b30fa408..0000000000 --- a/apollo-router/tests/snapshots/set_context__set_context_list.snap.new +++ /dev/null @@ -1,137 +0,0 @@ ---- -source: apollo-router/tests/set_context.rs -assertion_line: 88 -expression: response ---- -{ - "data": { - "t": { - "id": "1", - "uList": [ - null, - null, - null - ] - } - }, - "errors": [ - { - "message": "couldn't find mock for query {\"query\":\"query Query__Subgraph1__1($representations:[_Any!]!$contextualArgument_1_0:String){_entities(representations:$representations){...on U{field(a:$contextualArgument_1_0)}}}\",\"operationName\":\"Query__Subgraph1__1\",\"variables\":{\"contextualArgument_1_0\":\"prop value\",\"representations\":[{\"__typename\":\"U\",\"id\":\"1\"},{\"__typename\":\"U\",\"id\":\"2\"},{\"__typename\":\"U\",\"id\":\"3\"}]}}", - "extensions": { - "code": "FETCH_ERROR" - } - } - ], - "extensions": { - "valueCompletion": [ - { - "message": "Cannot return null for non-nullable field U.field", - "path": [ - "t", - "uList", - 0 - ] - }, - { - "message": "Cannot return null for non-nullable field U.field", - "path": [ - "t", - "uList", - 1 - ] - }, - { - "message": "Cannot return null for non-nullable field U.field", - "path": [ - "t", - "uList", - 2 - ] - } - ], - "apolloQueryPlan": { - "object": { - "kind": "QueryPlan", - "node": { - "kind": "Sequence", - "nodes": [ - { - "kind": "Fetch", - "serviceName": "Subgraph1", - "variableUsages": [], - "operation": "query Query__Subgraph1__0{t{prop id uList{__typename id}}}", - "operationName": "Query__Subgraph1__0", - "operationKind": "query", - "id": null, - "inputRewrites": null, - "outputRewrites": null, - "contextRewrites": null, - "schemaAwareHash": "904cce765299d4ec167658b15259c8bc8c3093af5bb33db4d2979827472646cd", - "authorization": { - "is_authenticated": false, - "scopes": [], - "policies": [] - } - }, - { - "kind": "Flatten", - "path": [ - "", - "t", - "uList", - "@" - ], - "node": { - "kind": "Fetch", - "serviceName": "Subgraph1", - "requires": [ - { - "kind": "InlineFragment", - "typeCondition": "U", - "selections": [ - { - "kind": "Field", - "name": "__typename" - }, - { - "kind": "Field", - "name": "id" - } - ] - } - ], - "variableUsages": [ - "contextualArgument_1_0" - ], - "operation": "query Query__Subgraph1__1($representations:[_Any!]!$contextualArgument_1_0:String){_entities(representations:$representations){...on U{field(a:$contextualArgument_1_0)}}}", - "operationName": "Query__Subgraph1__1", - "operationKind": "query", - "id": null, - "inputRewrites": null, - "outputRewrites": null, - "contextRewrites": [ - { - "kind": "KeyRenamer", - "path": [ - "..", - "... on T", - "prop" - ], - "renameKeyTo": "contextualArgument_1_0" - } - ], - "schemaAwareHash": "66b954f39aead8436321c671eb71e56ce15bbe0c7b82f06b2f8f70473ce1cb6e", - "authorization": { - "is_authenticated": false, - "scopes": [], - "policies": [] - } - } - } - ] - } - }, - "text": "QueryPlan {\n Sequence {\n Fetch(service: \"Subgraph1\") {\n {\n t {\n prop\n id\n uList {\n __typename\n id\n }\n }\n }\n },\n Flatten(path: \".t.uList.@\") {\n Fetch(service: \"Subgraph1\") {\n {\n ... on U {\n __typename\n id\n }\n } =>\n {\n ... on U {\n field(a: $contextualArgument_1_0)\n }\n }\n },\n },\n },\n}" - } - } -} From d413e8782e714b3ea82e1b82fe81cac69cc7a649 Mon Sep 17 00:00:00 2001 From: o0Ignition0o Date: Thu, 16 May 2024 15:28:18 +0200 Subject: [PATCH 37/69] wip --- apollo-router/tests/fixtures/set_context/one.json | 4 ++-- .../tests/fixtures/set_context/supergraph.graphql | 9 +++++---- .../set_context__set_context_list_of_lists.snap | 5 +++-- .../tests/snapshots/set_context__set_context_union.snap | 8 ++++---- 4 files changed, 14 insertions(+), 12 deletions(-) diff --git a/apollo-router/tests/fixtures/set_context/one.json b/apollo-router/tests/fixtures/set_context/one.json index 560c60491a..635ae847b6 100644 --- a/apollo-router/tests/fixtures/set_context/one.json +++ b/apollo-router/tests/fixtures/set_context/one.json @@ -193,7 +193,7 @@ }, { "request": { - "query": "query QueryLL__Subgraph1__1($representations: [_Any!]!, $contextualArgument_1_0_0: String, $contextualArgument_1_0_1: String) {\\n _0: _entities(representations: $representations) {\\n ... on U {\\n field(a: $contextualArgument_1_0_0)\\n }\\n }\\n _1: _entities(representations: $representations) {\\n ... on U {\\n field(a: $contextualArgument_1_0_1)\\n }\\n }\\n}", + "query": "query QueryLL__Subgraph1__1($representations: [_Any!]!, $contextualArgument_1_0_0: String, $contextualArgument_1_0_1: String) { _0: _entities(representations: $representations) { ... on U { field(a: $contextualArgument_1_0_0) } } _1: _entities(representations: $representations) { ... on U { field(a: $contextualArgument_1_0_1) } } }", "operationName": "QueryLL__Subgraph1__1", "variables": { "contextualArgument_1_0_0": "prop value 1", @@ -221,7 +221,7 @@ }, { "request": { - "query": "query QueryLL__Subgraph1__1($representations: [_Any!]!, $contextualArgument_1_0_0: String, $contextualArgument_1_0_1: String) {\\n _0: _entities(representations: $representations) {\\n ... on U {\\n field(a: $contextualArgument_1_0_0)\\n }\\n }\\n _1: _entities(representations: $representations) {\\n ... on U {\\n field(a: $contextualArgument_1_0_1)\\n }\\n }\\n}", + "query": "query QueryLL__Subgraph1__1($representations: [_Any!]!, $contextualArgument_1_0_0: String, $contextualArgument_1_0_1: String) { _0: _entities(representations: $representations) { ... on U { field(a: $contextualArgument_1_0_0) } } _1: _entities(representations: $representations) { ... on U { field(a: $contextualArgument_1_0_1) } } }", "operationName": "QueryLL__Subgraph1__1", "variables": { "contextualArgument_1_0_1": "prop value 2", diff --git a/apollo-router/tests/fixtures/set_context/supergraph.graphql b/apollo-router/tests/fixtures/set_context/supergraph.graphql index b2bab6b310..16b9ba0019 100644 --- a/apollo-router/tests/fixtures/set_context/supergraph.graphql +++ b/apollo-router/tests/fixtures/set_context/supergraph.graphql @@ -5,7 +5,7 @@ schema query: Query } -directive @context(name: String!) repeatable on INTERFACE | OBJECT +directive @context(name: String!) repeatable on INTERFACE | OBJECT | UNION directive @context__fromContext(field: String) on ARGUMENT_DEFINITION @@ -100,7 +100,8 @@ type Query @join__type(graph: SUBGRAPH1) @join__type(graph: SUBGRAPH2) { union K @join__type(graph: SUBGRAPH1) @join__unionMember(graph: SUBGRAPH1, member: "A") - @join__unionMember(graph: SUBGRAPH1, member: "B") = + @join__unionMember(graph: SUBGRAPH1, member: "B") + @context(name: "Subgraph1__context2") = A | B @@ -154,10 +155,10 @@ type V graph: SUBGRAPH1 contextArguments: [ { - context: "Subgraph1__context" + context: "Subgraph1__context2" name: "a" type: "String" - selection: "... on T { prop }" + selection: "... on A { prop } ... on B { prop }" } ] ) diff --git a/apollo-router/tests/snapshots/set_context__set_context_list_of_lists.snap b/apollo-router/tests/snapshots/set_context__set_context_list_of_lists.snap index 4edde5aec2..f02be15a65 100644 --- a/apollo-router/tests/snapshots/set_context__set_context_list_of_lists.snap +++ b/apollo-router/tests/snapshots/set_context__set_context_list_of_lists.snap @@ -79,7 +79,7 @@ expression: response "variableUsages": [ "contextualArgument_1_0" ], - "operation": "query QueryLL__Subgraph1__1($representations:[_Any!]!$contextualArgument_1_0:String!){_entities(representations:$representations){...on U{field(a:$contextualArgument_1_0)}}}", + "operation": "query QueryLL__Subgraph1__1($representations:[_Any!]!$contextualArgument_1_0:String){_entities(representations:$representations){...on U{field(a:$contextualArgument_1_0)}}}", "operationName": "QueryLL__Subgraph1__1", "operationKind": "query", "id": null, @@ -90,12 +90,13 @@ expression: response "kind": "KeyRenamer", "path": [ "..", + "... on T", "prop" ], "renameKeyTo": "contextualArgument_1_0" } ], - "schemaAwareHash": "1b34ea844758738c6cd197900b48a6a3717db97d30a43c77faf63b3a39f84dda", + "schemaAwareHash": "a9b24549250c12e38c398c32e9218134fab000be3b934ebc6bb38ea096343646", "authorization": { "is_authenticated": false, "scopes": [], diff --git a/apollo-router/tests/snapshots/set_context__set_context_union.snap b/apollo-router/tests/snapshots/set_context__set_context_union.snap index 4b1a41c4b4..79be1a287e 100644 --- a/apollo-router/tests/snapshots/set_context__set_context_union.snap +++ b/apollo-router/tests/snapshots/set_context__set_context_union.snap @@ -67,7 +67,7 @@ expression: response "variableUsages": [ "contextualArgument_1_1" ], - "operation": "query QueryUnion__Subgraph1__1($representations:[_Any!]!$contextualArgument_1_1:String!){_entities(representations:$representations){...on V{field(a:$contextualArgument_1_1)}}}", + "operation": "query QueryUnion__Subgraph1__1($representations:[_Any!]!$contextualArgument_1_1:String){_entities(representations:$representations){...on V{field(a:$contextualArgument_1_1)}}}", "operationName": "QueryUnion__Subgraph1__1", "operationKind": "query", "id": null, @@ -84,7 +84,7 @@ expression: response "renameKeyTo": "contextualArgument_1_1" } ], - "schemaAwareHash": "9ecc4a9a1ef31dd054ca44d1fa6cf12ca2198604116f40eb5a2dd0411454ce47", + "schemaAwareHash": "c50ca82d402a330c1b35a6d76332094c40b00d6dec6f6b2a9b0a32ced68f4e95", "authorization": { "is_authenticated": false, "scopes": [], @@ -121,7 +121,7 @@ expression: response "variableUsages": [ "contextualArgument_1_1" ], - "operation": "query QueryUnion__Subgraph1__2($representations:[_Any!]!$contextualArgument_1_1:String!){_entities(representations:$representations){...on V{field(a:$contextualArgument_1_1)}}}", + "operation": "query QueryUnion__Subgraph1__2($representations:[_Any!]!$contextualArgument_1_1:String){_entities(representations:$representations){...on V{field(a:$contextualArgument_1_1)}}}", "operationName": "QueryUnion__Subgraph1__2", "operationKind": "query", "id": null, @@ -138,7 +138,7 @@ expression: response "renameKeyTo": "contextualArgument_1_1" } ], - "schemaAwareHash": "0a393294385fd932d1123d1fe054146f9fcd25dd927b02a72beecb2d6c3d867e", + "schemaAwareHash": "ec99886497fee9b4f13565e19cadb13ae85c83de93acb53f298944b7a29e630e", "authorization": { "is_authenticated": false, "scopes": [], From dbf8ce09bbea21105ce070e6bcf25690ab696eb9 Mon Sep 17 00:00:00 2001 From: o0Ignition0o Date: Thu, 16 May 2024 16:26:02 +0200 Subject: [PATCH 38/69] wip --- apollo-router/src/json_ext.rs | 2 +- apollo-router/src/query_planner/fetch.rs | 29 ++-- .../src/query_planner/subgraph_context.rs | 153 ++++++++++-------- .../tests/fixtures/set_context/one.json | 2 +- 4 files changed, 106 insertions(+), 80 deletions(-) diff --git a/apollo-router/src/json_ext.rs b/apollo-router/src/json_ext.rs index b333ec90b1..e0b29f9ed4 100644 --- a/apollo-router/src/json_ext.rs +++ b/apollo-router/src/json_ext.rs @@ -1064,7 +1064,7 @@ impl Path { } self.clone() - } + } } impl FromIterator for Path { diff --git a/apollo-router/src/query_planner/fetch.rs b/apollo-router/src/query_planner/fetch.rs index a6cdc4214f..5cac89d7c1 100644 --- a/apollo-router/src/query_planner/fetch.rs +++ b/apollo-router/src/query_planner/fetch.rs @@ -268,7 +268,8 @@ impl Variables { context_rewrites: &Option>, ) -> Option { let body = request.body(); - let mut subgraph_context = SubgraphContext::new(data, current_dir, schema, context_rewrites); + let mut subgraph_context = + SubgraphContext::new(data, current_dir, schema, context_rewrites); if !requires.is_empty() { let mut variables = Object::with_capacity(1 + variable_usages.len()); @@ -311,7 +312,7 @@ impl Variables { Some(context) => context.add_variables_and_get_args(&mut variables), None => None, }; - + variables.insert("representations", representations); Some(Variables { variables, @@ -393,16 +394,26 @@ impl FetchNode { return (Value::Object(Object::default()), Vec::new()); } }; - - let aliased_operation = build_operation_with_aliasing(operation, &contextual_arguments, parameters.schema); + let alias_query_string; // this exists outside the if block to allow the as_str() to be longer lived - let query_str = if let Ok(op) = aliased_operation { - alias_query_string = op.serialize().no_indent().to_string(); - alias_query_string.as_str() + let aliased_operation = if let Some(ctx_arg) = contextual_arguments { + match build_operation_with_aliasing(operation, &ctx_arg, parameters.schema) { + Ok(op) => { + alias_query_string = op.serialize().no_indent().to_string(); + alias_query_string.as_str() + } + Err(errors) => { + tracing::debug!( + "couldn't generate a valid executable document? {:?}", + errors + ); + operation.as_serialized() + } + } } else { operation.as_serialized() }; - + let mut subgraph_request = SubgraphRequest::builder() .supergraph_request(parameters.supergraph_request.clone()) .subgraph_request( @@ -421,7 +432,7 @@ impl FetchNode { ) .body( Request::builder() - .query(query_str) + .query(aliased_operation) .and_operation_name(operation_name.as_ref().map(|n| n.to_string())) .variables(variables.clone()) .build(), diff --git a/apollo-router/src/query_planner/subgraph_context.rs b/apollo-router/src/query_planner/subgraph_context.rs index 4d9a2f2824..447c404b17 100644 --- a/apollo-router/src/query_planner/subgraph_context.rs +++ b/apollo-router/src/query_planner/subgraph_context.rs @@ -5,6 +5,9 @@ use apollo_compiler::executable; use apollo_compiler::executable::Operation; use apollo_compiler::executable::Selection; use apollo_compiler::executable::SelectionSet; +use apollo_compiler::validation::Valid; +use apollo_compiler::validation::WithErrors; +use apollo_compiler::ExecutableDocument; use apollo_compiler::Node; use serde_json_bytes::ByteString; use serde_json_bytes::Map; @@ -23,6 +26,7 @@ use crate::json_ext::Value; use crate::json_ext::ValueExt; use crate::spec::Schema; +#[derive(Debug)] pub(crate) struct ContextualArguments { pub(crate) arguments: HashSet, // a set of all argument names that will be passed to the subgraph. This is the unmodified name from the query plan pub(crate) count: usize, // the number of different sets of arguments that exist. This will either be 1 or the number of entities @@ -36,7 +40,7 @@ pub(crate) struct SubgraphContext<'a> { pub(crate) named_args: Vec>, } -// TODO: We're using ValueExt::get_path, so I believe this is no longer needed, but I'm +// TODO: We're using ValueExt::get_path, so I believe this is no longer needed, but I'm // going to keep it commented until all the tests are passing // fn data_at_path<'v>(data: &'v Value, path: &Path) -> Option<&'v Value> { // let v = match &path.0[0] { @@ -65,7 +69,7 @@ pub(crate) struct SubgraphContext<'a> { // v // } -// context_path is a non-standard relative path which may navigate up the tree +// context_path is a non-standard relative path which may navigate up the tree // from the current position. This is indicated with a ".." PathElement::Key // note that the return value is an absolute path that may be used anywhere fn merge_context_path(current_dir: &Path, context_path: &Path) -> Path { @@ -123,7 +127,7 @@ impl<'a> SubgraphContext<'a> { } // For each of the rewrites, start collecting data for the data at path. - // Once we find a Value for a given variable, skip additional rewrites that + // Once we find a Value for a given variable, skip additional rewrites that // reference the same variable pub(crate) fn execute_on_path(&mut self, path: &Path) { let mut found_rewrites: HashSet = HashSet::new(); @@ -136,7 +140,7 @@ impl<'a> SubgraphContext<'a> { if !found_rewrites.contains(item.rename_key_to.as_str()) { let data_path = merge_context_path(path, &item.path); let val = self.data.get_path(self.schema, &data_path); - + if let Ok(v) = val { // add to found found_rewrites.insert(item.rename_key_to.clone().to_string()); @@ -179,8 +183,8 @@ impl<'a> SubgraphContext<'a> { self.named_args.push(hash_map); } - // Once all a value has been extracted for every variable, go ahead and add all - // variables to the variables map. Additionally, return a ContextualArguments structure if + // Once all a value has been extracted for every variable, go ahead and add all + // variables to the variables map. Additionally, return a ContextualArguments structure if // values of variables are entity dependent pub(crate) fn add_variables_and_get_args( &self, @@ -232,62 +236,65 @@ impl<'a> SubgraphContext<'a> { // where we are collecting entites and different entities may have different variables passed to the resolver. pub(crate) fn build_operation_with_aliasing( subgraph_operation: &SubgraphOperation, - contextual_arguments: &Option, + contextual_arguments: &ContextualArguments, schema: &Schema, -) -> Result { +) -> Result, ContextBatchingError> { + dbg!( + &subgraph_operation.to_string(), + &contextual_arguments, + &schema.raw_sdl + ); let mut selections: Vec = vec![]; - match contextual_arguments { - Some(ContextualArguments { arguments, count }) => { - let parsed_document = subgraph_operation.as_parsed(schema.supergraph_schema()); - if let Ok(document) = parsed_document { - // TODO: Can there be more than one named operation? - // Can there be an anonymous operation? - if let Some((_, op)) = document.named_operations.first() { - let mut new_variables: Vec> = vec![]; - op.variables.iter().for_each(|v| { - if arguments.contains(v.name.as_str()) { - for i in 0..*count { - new_variables.push(Node::new(VariableDefinition { - name: Name::new_unchecked( - format!("{}_{}", v.name.as_str(), i).into(), - ), - ty: v.ty.clone(), - default_value: v.default_value.clone(), - directives: v.directives.clone(), - })); - } - } else { - new_variables.push(v.clone()); - } - }); - + let ContextualArguments { arguments, count } = contextual_arguments; + let parsed_document = subgraph_operation.as_parsed(schema.supergraph_schema()); + if let Ok(document) = parsed_document { + // TODO: Can there be more than one named operation? + // Can there be an anonymous operation? + if let Some((_, op)) = document.named_operations.first() { + let mut new_variables: Vec> = vec![]; + op.variables.iter().for_each(|v| { + if arguments.contains(v.name.as_str()) { for i in 0..*count { - // If we are aliasing, we know that there is only one selection in the top level SelectionSet - // it is a field selection for _entities, so it's ok to reach in and give it an alias - let mut selection_set = op.selection_set.clone(); - transform_selection_set(&mut selection_set, arguments, i, true); - selections.push(selection_set.selections[0].clone()) + new_variables.push(Node::new(VariableDefinition { + name: Name::new_unchecked(format!("{}_{}", v.name.as_str(), i).into()), + ty: v.ty.clone(), + default_value: v.default_value.clone(), + directives: v.directives.clone(), + })); } - - Ok(Operation { - operation_type: op.operation_type.clone(), - name: op.name.clone(), - directives: op.directives.clone(), - variables: new_variables, - selection_set: SelectionSet { - ty: op.selection_set.ty.clone(), - selections, - }, - }) - } else { // TODO: This is ugly, but I get compiler errors if the else blocks aren't present - Err(ContextBatchingError::NoSelectionSet) + } else { + new_variables.push(v.clone()); } - } else { - Err(ContextBatchingError::NoSelectionSet) + }); + + for i in 0..*count { + // If we are aliasing, we know that there is only one selection in the top level SelectionSet + // it is a field selection for _entities, so it's ok to reach in and give it an alias + let mut selection_set = op.selection_set.clone(); + transform_selection_set(&mut selection_set, arguments, i, true); + selections.push(selection_set.selections[0].clone()) } + + let mut ed = ExecutableDocument::new(); + ed.insert_operation(Operation { + operation_type: op.operation_type.clone(), + name: op.name.clone(), + directives: op.directives.clone(), + variables: new_variables, + selection_set: SelectionSet { + ty: op.selection_set.ty.clone(), + selections, + }, + }); + + let valid_document = ed + .validate(schema.supergraph_schema()) + .map_err(ContextBatchingError::InvalidDocumentGenerated)?; + + return Ok(valid_document); } - None => Err(ContextBatchingError::NoSelectionSet), } + Err(ContextBatchingError::NoSelectionSet) } fn add_alias_to_selection(selection: &mut executable::Field, index: usize) { @@ -340,22 +347,30 @@ fn transform_field_arguments( #[derive(Debug)] pub(crate) enum ContextBatchingError { NoSelectionSet, + InvalidDocumentGenerated(WithErrors), } -#[test] -fn test_merge_context_path() {} -// fn test_query_batching_for_contextual_args() { -// let old_query = "query QueryLL__Subgraph1__1($representations:[_Any!]!$Subgraph1_U_field_a:String!){_entities(representations:$representations){...on U{id field(a:$Subgraph1_U_field_a)}}}"; -// let mut ctx_args = HashSet::new(); -// ctx_args.insert("Subgraph1_U_field_a".to_string()); -// let contextual_args = Some((ctx_args, 2)); +#[cfg(test)] +mod subgraph_context_tests { + use super::*; -// let expected = "query QueryLL__Subgraph1__1($representations: [_Any!]!, $Subgraph1_U_field_a_0: String!, $Subgraph1_U_field_a_1: String!) { _0: _entities(representations: $representations) { ... on U { id field(a: $Subgraph1_U_field_a_0) } } _1: _entities(representations: $representations) { ... on U { id field(a: $Subgraph1_U_field_a_1) } } }"; + // #[test] + // fn test_merge_context_path() { + // let old_query = "query QueryLL__Subgraph1__1($representations:[_Any!]!$Subgraph1_U_field_a:String!){_entities(representations:$representations){...on U{id field(a:$Subgraph1_U_field_a)}}}"; + // let mut ctx_args = HashSet::new(); + // ctx_args.insert("Subgraph1_U_field_a".to_string()); + // let contextual_args = Some((ctx_args, 2)); -// assert_eq!( -// expected, -// query_batching_for_contextual_args(old_query, &contextual_args) -// .unwrap() -// .unwrap() -// ); -// } + // let expected = "query QueryLL__Subgraph1__1($representations: [_Any!]!, $Subgraph1_U_field_a_0: String!, $Subgraph1_U_field_a_1: String!) { _0: _entities(representations: $representations) { ... on U { id field(a: $Subgraph1_U_field_a_0) } } _1: _entities(representations: $representations) { ... on U { id field(a: $Subgraph1_U_field_a_1) } } }"; + + // assert_eq!( + // expected, + // build_operation_with_aliasing(old_query, &contextual_args) + // .unwrap() + // .serialize() + // .no_indent() + // .to_string() + // .as_str() + // ); + // } +} diff --git a/apollo-router/tests/fixtures/set_context/one.json b/apollo-router/tests/fixtures/set_context/one.json index 635ae847b6..a8e4b68b0e 100644 --- a/apollo-router/tests/fixtures/set_context/one.json +++ b/apollo-router/tests/fixtures/set_context/one.json @@ -193,7 +193,7 @@ }, { "request": { - "query": "query QueryLL__Subgraph1__1($representations: [_Any!]!, $contextualArgument_1_0_0: String, $contextualArgument_1_0_1: String) { _0: _entities(representations: $representations) { ... on U { field(a: $contextualArgument_1_0_0) } } _1: _entities(representations: $representations) { ... on U { field(a: $contextualArgument_1_0_1) } } }", + "query": "query QueryLL__Subgraph1__1($representations:[_Any!]!$contextualArgument_1_0:String){_entities(representations:$representations){...on U{field(a:$contextualArgument_1_0)}}}", "operationName": "QueryLL__Subgraph1__1", "variables": { "contextualArgument_1_0_0": "prop value 1", From 65473b173dfd9e5f4c16c38863ebac6271b0310f Mon Sep 17 00:00:00 2001 From: Chris Lenfest Date: Thu, 16 May 2024 09:30:29 -0500 Subject: [PATCH 39/69] adding set context to license_enforcement --- .../src/uplink/license_enforcement.rs | 42 +++++ ...icense_enforcement__test__set_context.snap | 12 ++ .../src/uplink/testdata/set_context.graphql | 165 ++++++++++++++++++ 3 files changed, 219 insertions(+) create mode 100644 apollo-router/src/uplink/snapshots/apollo_router__uplink__license_enforcement__test__set_context.snap create mode 100644 apollo-router/src/uplink/testdata/set_context.graphql diff --git a/apollo-router/src/uplink/license_enforcement.rs b/apollo-router/src/uplink/license_enforcement.rs index 50b6c410c6..ecc671cfad 100644 --- a/apollo-router/src/uplink/license_enforcement.rs +++ b/apollo-router/src/uplink/license_enforcement.rs @@ -408,6 +408,19 @@ impl LicenseEnforcementReport { }], }, }, + SchemaRestriction::Spec { + name: "context".to_string(), + spec_url: "https://specs.apollo.dev/context".to_string(), + version_req: semver::VersionReq { + comparators: vec![semver::Comparator { + op: semver::Op::Exact, + major: 0, + minor: 1.into(), + patch: 0.into(), + pre: semver::Prerelease::EMPTY, + }], + }, + }, SchemaRestriction::Spec { name: "requiresScopes".to_string(), spec_url: "https://specs.apollo.dev/requiresScopes".to_string(), @@ -436,6 +449,21 @@ impl LicenseEnforcementReport { }, explanation: "The `overrideLabel` argument on the join spec's @field directive is restricted to Enterprise users. This argument exists in your supergraph as a result of using the `@override` directive with the `label` argument in one or more of your subgraphs.".to_string() }, + SchemaRestriction::DirectiveArgument { + name: "field".to_string(), + argument: "contextArguments".to_string(), + spec_url: "https://specs.apollo.dev/join".to_string(), + version_req: semver::VersionReq { + comparators: vec![semver::Comparator { + op: semver::Op::GreaterEq, + major: 0, + minor: 5.into(), + patch: 0.into(), + pre: semver::Prerelease::EMPTY, + }], + }, + explanation: "The `contextArguments` argument on the join spec's @field directive is restricted to Enterprise users. This argument exists in your supergraph as a result of using the `@fromContext` directive in one or more of your subgraphs.".to_string() + }, ] } } @@ -785,6 +813,20 @@ mod test { assert_snapshot!(report.to_string()); } + #[test] + fn set_context() { + let report = check( + include_str!("testdata/oss.router.yaml"), + include_str!("testdata/set_context.graphql"), + ); + + assert!( + !report.restricted_schema_in_use.is_empty(), + "should have found restricted features" + ); + assert_snapshot!(report.to_string()); + } + #[test] fn progressive_override_with_renamed_join_spec() { let report = check( diff --git a/apollo-router/src/uplink/snapshots/apollo_router__uplink__license_enforcement__test__set_context.snap b/apollo-router/src/uplink/snapshots/apollo_router__uplink__license_enforcement__test__set_context.snap new file mode 100644 index 0000000000..79ea2bbce0 --- /dev/null +++ b/apollo-router/src/uplink/snapshots/apollo_router__uplink__license_enforcement__test__set_context.snap @@ -0,0 +1,12 @@ +--- +source: apollo-router/src/uplink/license_enforcement.rs +expression: report.to_string() +--- +Schema features: +* @context + https://specs.apollo.dev/context/v0.1 + +* @join__field.contextArguments + https://specs.apollo.dev/join/v0.5 + +The `contextArguments` argument on the join spec's @field directive is restricted to Enterprise users. This argument exists in your supergraph as a result of using the `@fromContext` directive in one or more of your subgraphs. diff --git a/apollo-router/src/uplink/testdata/set_context.graphql b/apollo-router/src/uplink/testdata/set_context.graphql new file mode 100644 index 0000000000..16b9ba0019 --- /dev/null +++ b/apollo-router/src/uplink/testdata/set_context.graphql @@ -0,0 +1,165 @@ +schema + @link(url: "https://specs.apollo.dev/link/v1.0") + @link(url: "https://specs.apollo.dev/join/v0.5", for: EXECUTION) + @link(url: "https://specs.apollo.dev/context/v0.1", for: SECURITY) { + query: Query +} + +directive @context(name: String!) repeatable on INTERFACE | OBJECT | UNION + +directive @context__fromContext(field: String) on ARGUMENT_DEFINITION + +directive @join__directive( + graphs: [join__Graph!] + name: String! + args: join__DirectiveArguments +) repeatable on SCHEMA | OBJECT | INTERFACE | FIELD_DEFINITION + +directive @join__enumValue(graph: join__Graph!) repeatable on ENUM_VALUE + +directive @join__field( + graph: join__Graph + requires: join__FieldSet + provides: join__FieldSet + type: String + external: Boolean + override: String + usedOverridden: Boolean + overrideLabel: String + contextArguments: [join__ContextArgument!] +) repeatable on FIELD_DEFINITION | INPUT_FIELD_DEFINITION + +directive @join__graph(name: String!, url: String!) on ENUM_VALUE + +directive @join__implements( + graph: join__Graph! + interface: String! +) repeatable on OBJECT | INTERFACE + +directive @join__type( + graph: join__Graph! + key: join__FieldSet + extension: Boolean! = false + resolvable: Boolean! = true + isInterfaceObject: Boolean! = false +) repeatable on OBJECT | INTERFACE | UNION | ENUM | INPUT_OBJECT | SCALAR + +directive @join__unionMember( + graph: join__Graph! + member: String! +) repeatable on UNION + +directive @link( + url: String + as: String + for: link__Purpose + import: [link__Import] +) repeatable on SCHEMA + +scalar context__context + +input join__ContextArgument { + name: String! + type: String! + context: String! + selection: join__FieldValue! +} + +scalar join__DirectiveArguments + +scalar join__FieldSet + +scalar join__FieldValue + +enum join__Graph { + SUBGRAPH1 @join__graph(name: "Subgraph1", url: "https://Subgraph1") + SUBGRAPH2 @join__graph(name: "Subgraph2", url: "https://Subgraph2") +} + +scalar link__Import + +enum link__Purpose { + """ + `SECURITY` features provide metadata necessary to securely resolve fields. + """ + SECURITY + + """ + `EXECUTION` features provide metadata necessary for operation execution. + """ + EXECUTION +} + +type Query @join__type(graph: SUBGRAPH1) @join__type(graph: SUBGRAPH2) { + t: T! @join__field(graph: SUBGRAPH1) + tList: [T]! @join__field(graph: SUBGRAPH1) + a: Int! @join__field(graph: SUBGRAPH2) + k: K! @join__field(graph: SUBGRAPH1) +} + +union K + @join__type(graph: SUBGRAPH1) + @join__unionMember(graph: SUBGRAPH1, member: "A") + @join__unionMember(graph: SUBGRAPH1, member: "B") + @context(name: "Subgraph1__context2") = + A + | B + +type A @join__type(graph: SUBGRAPH1, key: "id") { + id: ID! + v: V! + prop: String! +} + +type B @join__type(graph: SUBGRAPH1, key: "id") { + id: ID! + v: V! + prop: String! +} + +type T + @join__type(graph: SUBGRAPH1, key: "id") + @context(name: "Subgraph1__context") { + id: ID! + u: U! + uList: [U]! + prop: String! +} + +type U + @join__type(graph: SUBGRAPH1, key: "id") + @join__type(graph: SUBGRAPH2, key: "id") { + id: ID! + b: String! @join__field(graph: SUBGRAPH2) + field: Int! + @join__field( + graph: SUBGRAPH1 + contextArguments: [ + { + context: "Subgraph1__context" + name: "a" + type: "String" + selection: "{ prop }" + } + ] + ) +} + +type V + @join__type(graph: SUBGRAPH1, key: "id") + @join__type(graph: SUBGRAPH2, key: "id") { + id: ID! + b: String! @join__field(graph: SUBGRAPH2) + field: Int! + @join__field( + graph: SUBGRAPH1 + contextArguments: [ + { + context: "Subgraph1__context2" + name: "a" + type: "String" + selection: "... on A { prop } ... on B { prop }" + } + ] + ) +} From 67ffcc3d5c1be5761cb277210ba62ae86cb035f0 Mon Sep 17 00:00:00 2001 From: Chris Lenfest Date: Thu, 16 May 2024 23:47:39 -0500 Subject: [PATCH 40/69] checkpoint - use real version of router-bridge 2.8.0.alpha.0 - add some more tests - fix up rewrites to use existing api - DRY tests - add comments --- Cargo.lock | 5 +- apollo-router/Cargo.toml | 2 +- apollo-router/build/main.rs | 22 +-- apollo-router/src/query_planner/fetch.rs | 3 +- apollo-router/src/query_planner/rewrites.rs | 6 +- .../src/query_planner/subgraph_context.rs | 135 +++++++------- .../tests/fixtures/set_context/one.json | 61 ++++--- apollo-router/tests/set_context.rs | 166 +++++++++--------- .../set_context__set_context_list.snap | 6 +- ...et_context__set_context_list_of_lists.snap | 6 +- ...ontext__set_context_list_of_lists.snap.new | 138 +++++++++++++++ ...set_context__set_context_no_typenames.snap | 6 +- .../set_context__set_context_union.snap | 6 +- .../set_context__set_context_with_null.snap | 99 +++++++++++ fuzz/Cargo.toml | 2 +- 15 files changed, 454 insertions(+), 209 deletions(-) create mode 100644 apollo-router/tests/snapshots/set_context__set_context_list_of_lists.snap.new create mode 100644 apollo-router/tests/snapshots/set_context__set_context_with_null.snap diff --git a/Cargo.lock b/Cargo.lock index f86d403bb8..74066da56a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5595,8 +5595,9 @@ dependencies = [ [[package]] name = "router-bridge" -version = "0.5.21+v2.7.5" -source = "git+https://github.com/apollographql/federation-rs?rev=ced6eed5c4394bb3cbf3867798b8b7f63cb82d66#ced6eed5c4394bb3cbf3867798b8b7f63cb82d66" +version = "0.5.23+v2.8.0-alpha.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6824bf0a836f1df5b4091ff83efeedbf2aa56ab29baaff840b8f9053563d5c24" dependencies = [ "anyhow", "async-channel 1.9.0", diff --git a/apollo-router/Cargo.toml b/apollo-router/Cargo.toml index 0bbc381e9e..670b6a4501 100644 --- a/apollo-router/Cargo.toml +++ b/apollo-router/Cargo.toml @@ -185,7 +185,7 @@ regex = "1.10.3" reqwest.workspace = true # note: this dependency should _always_ be pinned, prefix the version with an `=` -router-bridge = { git = "https://github.com/apollographql/federation-rs", rev = "ced6eed5c4394bb3cbf3867798b8b7f63cb82d66" } +router-bridge = "=0.5.23+v2.8.0-alpha.0" rust-embed = "8.2.0" rustls = "0.21.11" diff --git a/apollo-router/build/main.rs b/apollo-router/build/main.rs index 844a2fab37..763d894df0 100644 --- a/apollo-router/build/main.rs +++ b/apollo-router/build/main.rs @@ -10,23 +10,23 @@ fn main() -> Result<(), Box> { ) .expect("could not parse Cargo.toml"); - let _router_bridge = cargo_manifest + let router_bridge = cargo_manifest .get("dependencies") .expect("Cargo.toml does not contain dependencies") .as_object() .expect("Cargo.toml dependencies key is not an object") .get("router-bridge") .expect("Cargo.toml dependencies does not have an entry for router-bridge"); - // let router_bridge_version = router_bridge - // .as_str() - // .or_else(|| { - // router_bridge - // .as_object() - // .and_then(|o| o.get("version")) - // .and_then(|version| version.as_str()) - // }) - // .expect("router-bridge does not have a version"); - let router_bridge_version = "0.5.20+v2.8.0"; + let router_bridge_version = router_bridge + .as_str() + .or_else(|| { + router_bridge + .as_object() + .and_then(|o| o.get("version")) + .and_then(|version| version.as_str()) + }) + .expect("router-bridge does not have a version"); + let mut it = router_bridge_version.split('+'); let _ = it.next(); let fed_version = it.next().expect("invalid router-bridge version format"); diff --git a/apollo-router/src/query_planner/fetch.rs b/apollo-router/src/query_planner/fetch.rs index 5cac89d7c1..1d27725d08 100644 --- a/apollo-router/src/query_planner/fetch.rs +++ b/apollo-router/src/query_planner/fetch.rs @@ -268,8 +268,7 @@ impl Variables { context_rewrites: &Option>, ) -> Option { let body = request.body(); - let mut subgraph_context = - SubgraphContext::new(data, current_dir, schema, context_rewrites); + let mut subgraph_context = SubgraphContext::new(data, schema, context_rewrites); if !requires.is_empty() { let mut variables = Object::with_capacity(1 + variable_usages.len()); diff --git a/apollo-router/src/query_planner/rewrites.rs b/apollo-router/src/query_planner/rewrites.rs index b90c487217..f3b10a7fa4 100644 --- a/apollo-router/src/query_planner/rewrites.rs +++ b/apollo-router/src/query_planner/rewrites.rs @@ -47,7 +47,7 @@ pub(crate) struct DataKeyRenamer { } impl DataRewrite { - fn maybe_apply(&self, schema: &Schema, data: &mut Value) { + pub(crate) fn maybe_apply(&self, schema: &Schema, data: &mut Value) { match self { DataRewrite::ValueSetter(setter) => { // The `path` of rewrites can only be either `Key` or `Fragment`, and so far @@ -107,10 +107,6 @@ pub(crate) fn apply_rewrites( } } -pub(crate) fn apply_single_rewrite(schema: &Schema, value: &mut Value, rewrite: &DataRewrite) { - rewrite.maybe_apply(schema, value); -} - #[cfg(test)] mod tests { use serde_json_bytes::json; diff --git a/apollo-router/src/query_planner/subgraph_context.rs b/apollo-router/src/query_planner/subgraph_context.rs index 447c404b17..d6e7902bc1 100644 --- a/apollo-router/src/query_planner/subgraph_context.rs +++ b/apollo-router/src/query_planner/subgraph_context.rs @@ -15,10 +15,8 @@ use std::collections::HashMap; use std::collections::HashSet; use super::fetch::SubgraphOperation; -use super::rewrites; use super::rewrites::DataKeyRenamer; use super::rewrites::DataRewrite; -// use crate::json_ext::Object; use crate::json_ext::Path; use crate::json_ext::PathElement; @@ -34,45 +32,18 @@ pub(crate) struct ContextualArguments { pub(crate) struct SubgraphContext<'a> { pub(crate) data: &'a Value, - pub(crate) current_dir: &'a Path, pub(crate) schema: &'a Schema, pub(crate) context_rewrites: &'a Vec, pub(crate) named_args: Vec>, } -// TODO: We're using ValueExt::get_path, so I believe this is no longer needed, but I'm -// going to keep it commented until all the tests are passing -// fn data_at_path<'v>(data: &'v Value, path: &Path) -> Option<&'v Value> { -// let v = match &path.0[0] { -// PathElement::Fragment(s) => { -// // get the value at data.get("__typename") and compare it with s. If the values are equal, return data, otherwise None -// let mut result: Option<&Value> = None; -// let wrapped_typename = data.get("__typename"); -// if let Some(t) = wrapped_typename { -// if t.as_str() == Some(s.as_str()) { -// result = Some(data); -// } -// } -// result -// } -// PathElement::Key(v, _) => data.get(v), -// PathElement::Index(idx) => Some(&data[idx]), -// PathElement::Flatten(_) => None, -// }; - -// if path.len() > 1 { -// if let Some(val) = v { -// let remaining_path = path.iter().skip(1).cloned().collect(); -// return data_at_path(val, &remaining_path); -// } -// } -// v -// } - // context_path is a non-standard relative path which may navigate up the tree // from the current position. This is indicated with a ".." PathElement::Key // note that the return value is an absolute path that may be used anywhere -fn merge_context_path(current_dir: &Path, context_path: &Path) -> Path { +fn merge_context_path( + current_dir: &Path, + context_path: &Path, +) -> Result { let mut i = 0; let mut j = current_dir.len(); // iterate over the context_path(i), every time we encounter a '..', we want @@ -83,7 +54,11 @@ fn merge_context_path(current_dir: &Path, context_path: &Path) -> Path { let mut found = false; if e == ".." { while !found { + if j == 0 { + return Err(ContextBatchingError::InvalidRelativePath); + } j -= 1; + if let Some(PathElement::Key(_, _)) = current_dir.0.get(j) { found = true; } @@ -102,13 +77,12 @@ fn merge_context_path(current_dir: &Path, context_path: &Path) -> Path { context_path.iter().skip(i).for_each(|e| { return_path.push(e.clone()); }); - Path(return_path.into_iter().collect()) + Ok(Path(return_path.into_iter().collect())) } impl<'a> SubgraphContext<'a> { pub(crate) fn new( data: &'a Value, - current_dir: &'a Path, schema: &'a Schema, context_rewrites: &'a Option>, ) -> Option> { @@ -116,7 +90,6 @@ impl<'a> SubgraphContext<'a> { if rewrites.len() > 0 { return Some(SubgraphContext { data, - current_dir, schema, context_rewrites: rewrites, named_args: Vec::new(), @@ -138,40 +111,36 @@ impl<'a> SubgraphContext<'a> { match rewrite { DataRewrite::KeyRenamer(item) => { if !found_rewrites.contains(item.rename_key_to.as_str()) { - let data_path = merge_context_path(path, &item.path); - let val = self.data.get_path(self.schema, &data_path); + let wrapped_data_path = merge_context_path(path, &item.path); + if let Ok(data_path) = wrapped_data_path { + let val = self.data.get_path(self.schema, &data_path); - if let Ok(v) = val { - // add to found - found_rewrites.insert(item.rename_key_to.clone().to_string()); - // TODO: not great - let mut new_value = v.clone(); - if let Some(values) = new_value.as_array_mut() { - for v in values { - rewrites::apply_single_rewrite( - self.schema, - v, - &DataRewrite::KeyRenamer({ + if let Ok(v) = val { + // add to found + found_rewrites.insert(item.rename_key_to.clone().to_string()); + // TODO: not great + let mut new_value = v.clone(); + if let Some(values) = new_value.as_array_mut() { + for v in values { + let data_rewrite = DataRewrite::KeyRenamer({ DataKeyRenamer { path: data_path.clone(), rename_key_to: item.rename_key_to.clone(), } - }), - ); - } - } else { - rewrites::apply_single_rewrite( - self.schema, - &mut new_value, - &DataRewrite::KeyRenamer({ + }); + data_rewrite.maybe_apply(self.schema, v); + } + } else { + let data_rewrite = DataRewrite::KeyRenamer({ DataKeyRenamer { - path: data_path, + path: data_path.clone(), rename_key_to: item.rename_key_to.clone(), } - }), - ); + }); + data_rewrite.maybe_apply(self.schema, &mut new_value); + } + return Some((item.rename_key_to.to_string(), new_value)); } - return Some((item.rename_key_to.to_string(), new_value)); } } None @@ -239,14 +208,10 @@ pub(crate) fn build_operation_with_aliasing( contextual_arguments: &ContextualArguments, schema: &Schema, ) -> Result, ContextBatchingError> { - dbg!( - &subgraph_operation.to_string(), - &contextual_arguments, - &schema.raw_sdl - ); let mut selections: Vec = vec![]; let ContextualArguments { arguments, count } = contextual_arguments; - let parsed_document = subgraph_operation.as_parsed(schema.supergraph_schema()); + let parsed_document = + subgraph_operation.as_parsed(schema.federation_supergraph().schema.schema()); if let Ok(document) = parsed_document { // TODO: Can there be more than one named operation? // Can there be an anonymous operation? @@ -288,7 +253,7 @@ pub(crate) fn build_operation_with_aliasing( }); let valid_document = ed - .validate(schema.supergraph_schema()) + .validate(schema.federation_supergraph().schema.schema()) .map_err(ContextBatchingError::InvalidDocumentGenerated)?; return Ok(valid_document); @@ -297,10 +262,14 @@ pub(crate) fn build_operation_with_aliasing( Err(ContextBatchingError::NoSelectionSet) } +// adds an alias that aligns with the index for this selection fn add_alias_to_selection(selection: &mut executable::Field, index: usize) { selection.alias = Some(Name::new_unchecked(format!("_{}", index).into())); } +// This function will take the selection set (which has been cloned from the original) +// and transform it so that all contextual variables in the selection set will be appended with a _ +// to match the index in the alias that it is fn transform_selection_set( selection_set: &mut SelectionSet, arguments: &HashSet, @@ -327,6 +296,7 @@ fn transform_selection_set( }); } +// transforms the variable name on the field argment fn transform_field_arguments( arguments_in_selection: &mut Vec>, arguments: &HashSet, @@ -348,12 +318,39 @@ fn transform_field_arguments( pub(crate) enum ContextBatchingError { NoSelectionSet, InvalidDocumentGenerated(WithErrors), + InvalidRelativePath, } #[cfg(test)] -mod subgraph_context_tests { +mod subgraph_context_unit_tests { use super::*; + #[test] + fn test_merge_context_path() { + let current_dir: Path = serde_json::from_str(r#"["t","u"]"#).unwrap(); + let relative_path: Path = serde_json::from_str(r#"["..","... on T","prop"]"#).unwrap(); + let expected = r#"["t","... on T","prop"]"#; + + let result = merge_context_path(¤t_dir, &relative_path).unwrap(); + assert_eq!(expected, serde_json::to_string(&result).unwrap(),); + } + + #[test] + fn test_merge_context_path_invalid() { + let current_dir: Path = serde_json::from_str(r#"["t","u"]"#).unwrap(); + let relative_path: Path = + serde_json::from_str(r#"["..","..","..","... on T","prop"]"#).unwrap(); + + let result = merge_context_path(¤t_dir, &relative_path); + match result { + Ok(_) => panic!("Expected an error, but got Ok"), + Err(e) => match e { + ContextBatchingError::InvalidRelativePath => (), + _ => panic!("Expected InvalidRelativePath, but got a different error"), + }, + } + } + // #[test] // fn test_merge_context_path() { // let old_query = "query QueryLL__Subgraph1__1($representations:[_Any!]!$Subgraph1_U_field_a:String!){_entities(representations:$representations){...on U{id field(a:$Subgraph1_U_field_a)}}}"; diff --git a/apollo-router/tests/fixtures/set_context/one.json b/apollo-router/tests/fixtures/set_context/one.json index a8e4b68b0e..1245129c99 100644 --- a/apollo-router/tests/fixtures/set_context/one.json +++ b/apollo-router/tests/fixtures/set_context/one.json @@ -21,12 +21,13 @@ }, { "request": { - "query": "query Query__Subgraph1__0{t{prop id u{__typename id}}}", + "query": "query Query__Subgraph1__0{t{__typename prop id u{__typename id}}}", "operationName": "Query__Subgraph1__0" }, "response": { "data": { "t": { + "__typename": "T", "prop": "prop value", "id": "1", "u": { @@ -39,12 +40,13 @@ }, { "request": { - "query": "query Query__Subgraph1__0{t{prop id uList{__typename id}}}", + "query": "query Query__Subgraph1__0{t{__typename prop id uList{__typename id}}}", "operationName": "Query__Subgraph1__0" }, "response": { "data": { "t": { + "__typename": "T", "prop": "prop value", "id": "1", "uList": [ @@ -67,7 +69,7 @@ }, { "request": { - "query": "query QueryLL__Subgraph1__0{tList{prop id uList{__typename id}}}", + "query": "query QueryLL__Subgraph1__0{tList{__typename prop id uList{__typename id}}}", "operationName": "QueryLL__Subgraph1__0" }, "response": { @@ -101,7 +103,7 @@ }, { "request": { - "query": "query QueryUnion__Subgraph1__0{k{__typename ...on A{prop v{__typename id}}...on B{prop v{__typename id}}}}", + "query": "query QueryUnion__Subgraph1__0{k{__typename ...on A{__typename prop v{__typename id}}...on B{__typename prop v{__typename id}}}}", "operationName": "QueryUnion__Subgraph1__0" }, "response": { @@ -193,11 +195,11 @@ }, { "request": { - "query": "query QueryLL__Subgraph1__1($representations:[_Any!]!$contextualArgument_1_0:String){_entities(representations:$representations){...on U{field(a:$contextualArgument_1_0)}}}", + "query": "query QueryLL__Subgraph1__1($representations: [_Any!]!, $contextualArgument_1_0_0: String, $contextualArgument_1_0_1: String) { _0: _entities(representations: $representations) { ... on U { field(a: $contextualArgument_1_0_0) } } _1: _entities(representations: $representations) { ... on U { field(a: $contextualArgument_1_0_1) } } }", "operationName": "QueryLL__Subgraph1__1", "variables": { - "contextualArgument_1_0_0": "prop value 1", "contextualArgument_1_0_1": "prop value 2", + "contextualArgument_1_0_0": "prop value 1", "representations": [ { "__typename": "U", "id": "3" }, { "__typename": "U", "id": "4" } @@ -221,15 +223,11 @@ }, { "request": { - "query": "query QueryLL__Subgraph1__1($representations: [_Any!]!, $contextualArgument_1_0_0: String, $contextualArgument_1_0_1: String) { _0: _entities(representations: $representations) { ... on U { field(a: $contextualArgument_1_0_0) } } _1: _entities(representations: $representations) { ... on U { field(a: $contextualArgument_1_0_1) } } }", - "operationName": "QueryLL__Subgraph1__1", + "query": "query QueryUnion__Subgraph1__1($representations:[_Any!]!$contextualArgument_1_1:String){_entities(representations:$representations){...on V{field(a:$contextualArgument_1_1)}}}", + "operationName": "QueryUnion__Subgraph1__1", "variables": { - "contextualArgument_1_0_1": "prop value 2", - "contextualArgument_1_0_0": "prop value 1", - "representations": [ - { "__typename": "U", "id": "3" }, - { "__typename": "U", "id": "4" } - ] + "contextualArgument_1_1": "prop value 3", + "representations": [{ "__typename": "V", "id": "2" }] } }, "response": { @@ -238,10 +236,6 @@ { "id": "3", "field": 3456 - }, - { - "id": "4", - "field": 4567 } ] } @@ -249,19 +243,38 @@ }, { "request": { - "query": "query QueryUnion__Subgraph1__1($representations:[_Any!]!$contextualArgument_1_1:String){_entities(representations:$representations){...on V{field(a:$contextualArgument_1_1)}}}", - "operationName": "QueryUnion__Subgraph1__1", + "query": "query Query_Null_Param__Subgraph1__0{t{__typename prop id u{__typename id}}}", + "operationName": "Query_Null_Param__Subgraph1__0" + }, + "response": { + "data": { + "t": { + "__typename": "T", + "prop": null, + "id": "1", + "u": { + "__typename": "U", + "id": "1" + } + } + } + } + }, + { + "request": { + "query": "query Query_Null_Param__Subgraph1__1($representations:[_Any!]!$contextualArgument_1_0:String){_entities(representations:$representations){...on U{field(a:$contextualArgument_1_0)}}}", + "operationName": "Query_Null_Param__Subgraph1__1", "variables": { - "contextualArgument_1_1": "prop value 3", - "representations": [{ "__typename": "V", "id": "2" }] + "contextualArgument_1_0": null, + "representations": [{ "__typename": "U", "id": "1" }] } }, "response": { "data": { "_entities": [ { - "id": "3", - "field": 3456 + "id": "1", + "field": 1234 } ] } diff --git a/apollo-router/tests/set_context.rs b/apollo-router/tests/set_context.rs index c2bf12c142..fb1688ead7 100644 --- a/apollo-router/tests/set_context.rs +++ b/apollo-router/tests/set_context.rs @@ -56,6 +56,18 @@ async fn run_single_request(query: &str, mocks: &[(&'static str, &'static str)]) #[tokio::test(flavor = "multi_thread")] async fn test_set_context() { + static QUERY: &str = r#" + query Query { + t { + __typename + id + u { + __typename + field + } + } + }"#; + let response = run_single_request( QUERY, &[ @@ -70,6 +82,16 @@ async fn test_set_context() { #[tokio::test(flavor = "multi_thread")] async fn test_set_context_no_typenames() { + static QUERY_NO_TYPENAMES: &str = r#" + query Query { + t { + id + u { + field + } + } + }"#; + let response = run_single_request( QUERY_NO_TYPENAMES, &[ @@ -84,6 +106,16 @@ async fn test_set_context_no_typenames() { #[tokio::test(flavor = "multi_thread")] async fn test_set_context_list() { + static QUERY_WITH_LIST: &str = r#" + query Query { + t { + id + uList { + field + } + } + }"#; + let response = run_single_request( QUERY_WITH_LIST, &[ @@ -98,6 +130,16 @@ async fn test_set_context_list() { #[tokio::test(flavor = "multi_thread")] async fn test_set_context_list_of_lists() { + static QUERY_WITH_LIST_OF_LISTS: &str = r#" + query QueryLL { + tList { + id + uList { + field + } + } + }"#; + let response = run_single_request( QUERY_WITH_LIST_OF_LISTS, &[ @@ -112,37 +154,54 @@ async fn test_set_context_list_of_lists() { #[tokio::test(flavor = "multi_thread")] async fn test_set_context_union() { - let harness = setup_from_mocks( - json! {{ - "experimental_type_conditioned_fetching": true, - // will make debugging easier - "plugins": { - "experimental.expose_query_plan": true - }, - "include_subgraph_errors": { - "all": true + static QUERY_WITH_UNION: &str = r#" + query QueryUnion { + k { + ... on A { + v { + field + } + } + ... on B { + v { + field + } + } } - }}, + }"#; + + let response = run_single_request( + QUERY_WITH_UNION, &[ ("Subgraph1", include_str!("fixtures/set_context/one.json")), ("Subgraph2", include_str!("fixtures/set_context/two.json")), ], - ); - let supergraph_service = harness.build_supergraph().await.unwrap(); - let request: supergraph::Request = supergraph::Request::fake_builder() - .query(QUERY_WITH_UNION.to_string()) - .header("Apollo-Expose-Query-Plan", "true") - .variables(Default::default()) - .build() - .expect("expecting valid request"); + ) + .await; - let response = supergraph_service - .oneshot(request) - .await - .unwrap() - .next_response() - .await - .unwrap(); + insta::assert_json_snapshot!(response); +} + +#[tokio::test(flavor = "multi_thread")] +async fn test_set_context_with_null() { + static QUERY: &str = r#" + query Query_Null_Param { + t { + id + u { + field + } + } + }"#; + + let response = run_single_request( + QUERY, + &[ + ("Subgraph1", include_str!("fixtures/set_context/one.json")), + ("Subgraph2", include_str!("fixtures/set_context/two.json")), + ], + ) + .await; insta::assert_json_snapshot!(response); } @@ -176,60 +235,3 @@ fn setup_from_mocks( .schema(schema) .extra_plugin(mocked_subgraphs) } - -static QUERY: &str = r#" -query Query { - t { - __typename - id - u { - __typename - field - } - } -}"#; -static QUERY_NO_TYPENAMES: &str = r#" -query Query { - t { - id - u { - field - } - } -}"#; - -static QUERY_WITH_LIST: &str = r#" -query Query { - t { - id - uList { - field - } - } -}"#; - -static QUERY_WITH_LIST_OF_LISTS: &str = r#" -query QueryLL { - tList { - id - uList { - field - } - } -}"#; - -static QUERY_WITH_UNION: &str = r#" -query QueryUnion { - k { - ... on A { - v { - field - } - } - ... on B { - v { - field - } - } - } -}"#; diff --git a/apollo-router/tests/snapshots/set_context__set_context_list.snap b/apollo-router/tests/snapshots/set_context__set_context_list.snap index 2a89743739..b4f9c84d0c 100644 --- a/apollo-router/tests/snapshots/set_context__set_context_list.snap +++ b/apollo-router/tests/snapshots/set_context__set_context_list.snap @@ -30,14 +30,14 @@ expression: response "kind": "Fetch", "serviceName": "Subgraph1", "variableUsages": [], - "operation": "query Query__Subgraph1__0{t{prop id uList{__typename id}}}", + "operation": "query Query__Subgraph1__0{t{__typename prop id uList{__typename id}}}", "operationName": "Query__Subgraph1__0", "operationKind": "query", "id": null, "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "904cce765299d4ec167658b15259c8bc8c3093af5bb33db4d2979827472646cd", + "schemaAwareHash": "4f746b9319e3ca4f234269464b6815eb97782f2ffe36774b998e7fb78f30abef", "authorization": { "is_authenticated": false, "scopes": [], @@ -102,7 +102,7 @@ expression: response ] } }, - "text": "QueryPlan {\n Sequence {\n Fetch(service: \"Subgraph1\") {\n {\n t {\n prop\n id\n uList {\n __typename\n id\n }\n }\n }\n },\n Flatten(path: \".t.uList.@\") {\n Fetch(service: \"Subgraph1\") {\n {\n ... on U {\n __typename\n id\n }\n } =>\n {\n ... on U {\n field(a: $contextualArgument_1_0)\n }\n }\n },\n },\n },\n}" + "text": "QueryPlan {\n Sequence {\n Fetch(service: \"Subgraph1\") {\n {\n t {\n __typename\n prop\n id\n uList {\n __typename\n id\n }\n }\n }\n },\n Flatten(path: \".t.uList.@\") {\n Fetch(service: \"Subgraph1\") {\n {\n ... on U {\n __typename\n id\n }\n } =>\n {\n ... on U {\n field(a: $contextualArgument_1_0)\n }\n }\n },\n },\n },\n}" } } } diff --git a/apollo-router/tests/snapshots/set_context__set_context_list_of_lists.snap b/apollo-router/tests/snapshots/set_context__set_context_list_of_lists.snap index f02be15a65..a10f9a3646 100644 --- a/apollo-router/tests/snapshots/set_context__set_context_list_of_lists.snap +++ b/apollo-router/tests/snapshots/set_context__set_context_list_of_lists.snap @@ -34,14 +34,14 @@ expression: response "kind": "Fetch", "serviceName": "Subgraph1", "variableUsages": [], - "operation": "query QueryLL__Subgraph1__0{tList{prop id uList{__typename id}}}", + "operation": "query QueryLL__Subgraph1__0{tList{__typename prop id uList{__typename id}}}", "operationName": "QueryLL__Subgraph1__0", "operationKind": "query", "id": null, "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "20170491dab082e15802bd0b9e6abdf2c6803589cc969668e8b0bc8388af0542", + "schemaAwareHash": "babf88ea82c1330e535966572a55b03a2934097cd1cf905303b86ae7c197ccaf", "authorization": { "is_authenticated": false, "scopes": [], @@ -107,7 +107,7 @@ expression: response ] } }, - "text": "QueryPlan {\n Sequence {\n Fetch(service: \"Subgraph1\") {\n {\n tList {\n prop\n id\n uList {\n __typename\n id\n }\n }\n }\n },\n Flatten(path: \".tList.@.uList.@\") {\n Fetch(service: \"Subgraph1\") {\n {\n ... on U {\n __typename\n id\n }\n } =>\n {\n ... on U {\n field(a: $contextualArgument_1_0)\n }\n }\n },\n },\n },\n}" + "text": "QueryPlan {\n Sequence {\n Fetch(service: \"Subgraph1\") {\n {\n tList {\n __typename\n prop\n id\n uList {\n __typename\n id\n }\n }\n }\n },\n Flatten(path: \".tList.@.uList.@\") {\n Fetch(service: \"Subgraph1\") {\n {\n ... on U {\n __typename\n id\n }\n } =>\n {\n ... on U {\n field(a: $contextualArgument_1_0)\n }\n }\n },\n },\n },\n}" } } } diff --git a/apollo-router/tests/snapshots/set_context__set_context_list_of_lists.snap.new b/apollo-router/tests/snapshots/set_context__set_context_list_of_lists.snap.new new file mode 100644 index 0000000000..806ea05aff --- /dev/null +++ b/apollo-router/tests/snapshots/set_context__set_context_list_of_lists.snap.new @@ -0,0 +1,138 @@ +--- +source: apollo-router/tests/set_context.rs +assertion_line: 152 +expression: response +--- +{ + "data": { + "tList": [ + { + "id": "1", + "uList": [ + null + ] + }, + { + "id": "2", + "uList": [ + null + ] + } + ] + }, + "errors": [ + { + "message": "couldn't find mock for query {\"query\":\"query QueryLL__Subgraph1__1($representations:[_Any!]!$contextualArgument_1_0:String){_entities(representations:$representations){...on U{field(a:$contextualArgument_1_0)}}}\",\"operationName\":\"QueryLL__Subgraph1__1\",\"variables\":{\"contextualArgument_1_0_0\":\"prop value 1\",\"contextualArgument_1_0_1\":\"prop value 2\",\"representations\":[{\"__typename\":\"U\",\"id\":\"3\"},{\"__typename\":\"U\",\"id\":\"4\"}]}}", + "extensions": { + "code": "FETCH_ERROR" + } + } + ], + "extensions": { + "valueCompletion": [ + { + "message": "Cannot return null for non-nullable field U.field", + "path": [ + "tList", + 0, + "uList", + 0 + ] + }, + { + "message": "Cannot return null for non-nullable field U.field", + "path": [ + "tList", + 1, + "uList", + 0 + ] + } + ], + "apolloQueryPlan": { + "object": { + "kind": "QueryPlan", + "node": { + "kind": "Sequence", + "nodes": [ + { + "kind": "Fetch", + "serviceName": "Subgraph1", + "variableUsages": [], + "operation": "query QueryLL__Subgraph1__0{tList{__typename prop id uList{__typename id}}}", + "operationName": "QueryLL__Subgraph1__0", + "operationKind": "query", + "id": null, + "inputRewrites": null, + "outputRewrites": null, + "contextRewrites": null, + "schemaAwareHash": "babf88ea82c1330e535966572a55b03a2934097cd1cf905303b86ae7c197ccaf", + "authorization": { + "is_authenticated": false, + "scopes": [], + "policies": [] + } + }, + { + "kind": "Flatten", + "path": [ + "", + "tList", + "@", + "uList", + "@" + ], + "node": { + "kind": "Fetch", + "serviceName": "Subgraph1", + "requires": [ + { + "kind": "InlineFragment", + "typeCondition": "U", + "selections": [ + { + "kind": "Field", + "name": "__typename" + }, + { + "kind": "Field", + "name": "id" + } + ] + } + ], + "variableUsages": [ + "contextualArgument_1_0" + ], + "operation": "query QueryLL__Subgraph1__1($representations:[_Any!]!$contextualArgument_1_0:String){_entities(representations:$representations){...on U{field(a:$contextualArgument_1_0)}}}", + "operationName": "QueryLL__Subgraph1__1", + "operationKind": "query", + "id": null, + "inputRewrites": null, + "outputRewrites": null, + "contextRewrites": [ + { + "kind": "KeyRenamer", + "path": [ + "..", + "... on T", + "prop" + ], + "renameKeyTo": "contextualArgument_1_0" + } + ], + "schemaAwareHash": "a9b24549250c12e38c398c32e9218134fab000be3b934ebc6bb38ea096343646", + "authorization": { + "is_authenticated": false, + "scopes": [], + "policies": [] + } + } + } + ] + } + }, + "text": "QueryPlan {\n Sequence {\n Fetch(service: \"Subgraph1\") {\n {\n tList {\n __typename\n prop\n id\n uList {\n __typename\n id\n }\n }\n }\n },\n Flatten(path: \".tList.@.uList.@\") {\n Fetch(service: \"Subgraph1\") {\n {\n ... on U {\n __typename\n id\n }\n } =>\n {\n ... on U {\n field(a: $contextualArgument_1_0)\n }\n }\n },\n },\n },\n}" + } + } +} diff --git a/apollo-router/tests/snapshots/set_context__set_context_no_typenames.snap b/apollo-router/tests/snapshots/set_context__set_context_no_typenames.snap index b4dee09d70..d1c7e137fd 100644 --- a/apollo-router/tests/snapshots/set_context__set_context_no_typenames.snap +++ b/apollo-router/tests/snapshots/set_context__set_context_no_typenames.snap @@ -22,14 +22,14 @@ expression: response "kind": "Fetch", "serviceName": "Subgraph1", "variableUsages": [], - "operation": "query Query__Subgraph1__0{t{prop id u{__typename id}}}", + "operation": "query Query__Subgraph1__0{t{__typename prop id u{__typename id}}}", "operationName": "Query__Subgraph1__0", "operationKind": "query", "id": null, "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "c04be6c6131c93b4ac0d65b2ebed64669b8c74b826e1a70337f0b104932250f9", + "schemaAwareHash": "d7cb2d1809789d49360ca0a60570555f83855f00547675f366915c9d9d90fef9", "authorization": { "is_authenticated": false, "scopes": [], @@ -93,7 +93,7 @@ expression: response ] } }, - "text": "QueryPlan {\n Sequence {\n Fetch(service: \"Subgraph1\") {\n {\n t {\n prop\n id\n u {\n __typename\n id\n }\n }\n }\n },\n Flatten(path: \".t.u\") {\n Fetch(service: \"Subgraph1\") {\n {\n ... on U {\n __typename\n id\n }\n } =>\n {\n ... on U {\n field(a: $contextualArgument_1_0)\n }\n }\n },\n },\n },\n}" + "text": "QueryPlan {\n Sequence {\n Fetch(service: \"Subgraph1\") {\n {\n t {\n __typename\n prop\n id\n u {\n __typename\n id\n }\n }\n }\n },\n Flatten(path: \".t.u\") {\n Fetch(service: \"Subgraph1\") {\n {\n ... on U {\n __typename\n id\n }\n } =>\n {\n ... on U {\n field(a: $contextualArgument_1_0)\n }\n }\n },\n },\n },\n}" } } } diff --git a/apollo-router/tests/snapshots/set_context__set_context_union.snap b/apollo-router/tests/snapshots/set_context__set_context_union.snap index 79be1a287e..29df9c9a5e 100644 --- a/apollo-router/tests/snapshots/set_context__set_context_union.snap +++ b/apollo-router/tests/snapshots/set_context__set_context_union.snap @@ -21,14 +21,14 @@ expression: response "kind": "Fetch", "serviceName": "Subgraph1", "variableUsages": [], - "operation": "query QueryUnion__Subgraph1__0{k{__typename ...on A{prop v{__typename id}}...on B{prop v{__typename id}}}}", + "operation": "query QueryUnion__Subgraph1__0{k{__typename ...on A{__typename prop v{__typename id}}...on B{__typename prop v{__typename id}}}}", "operationName": "QueryUnion__Subgraph1__0", "operationKind": "query", "id": null, "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "0f1ba6ea48b6f8adfc469d87012031090fe5de3f5ba9de8a8823a2d139c0370e", + "schemaAwareHash": "b9124cd1daa6e8347175ffe2108670a31c73cbc983e7812ee39f415235541005", "authorization": { "is_authenticated": false, "scopes": [], @@ -151,7 +151,7 @@ expression: response ] } }, - "text": "QueryPlan {\n Sequence {\n Fetch(service: \"Subgraph1\") {\n {\n k {\n __typename\n ... on A {\n prop\n v {\n __typename\n id\n }\n }\n ... on B {\n prop\n v {\n __typename\n id\n }\n }\n }\n }\n },\n Parallel {\n Flatten(path: \".k|[A].v\") {\n Fetch(service: \"Subgraph1\") {\n {\n ... on V {\n __typename\n id\n }\n } =>\n {\n ... on V {\n field(a: $contextualArgument_1_1)\n }\n }\n },\n },\n Flatten(path: \".k|[B].v\") {\n Fetch(service: \"Subgraph1\") {\n {\n ... on V {\n __typename\n id\n }\n } =>\n {\n ... on V {\n field(a: $contextualArgument_1_1)\n }\n }\n },\n },\n },\n },\n}" + "text": "QueryPlan {\n Sequence {\n Fetch(service: \"Subgraph1\") {\n {\n k {\n __typename\n ... on A {\n __typename\n prop\n v {\n __typename\n id\n }\n }\n ... on B {\n __typename\n prop\n v {\n __typename\n id\n }\n }\n }\n }\n },\n Parallel {\n Flatten(path: \".k|[A].v\") {\n Fetch(service: \"Subgraph1\") {\n {\n ... on V {\n __typename\n id\n }\n } =>\n {\n ... on V {\n field(a: $contextualArgument_1_1)\n }\n }\n },\n },\n Flatten(path: \".k|[B].v\") {\n Fetch(service: \"Subgraph1\") {\n {\n ... on V {\n __typename\n id\n }\n } =>\n {\n ... on V {\n field(a: $contextualArgument_1_1)\n }\n }\n },\n },\n },\n },\n}" } } } diff --git a/apollo-router/tests/snapshots/set_context__set_context_with_null.snap b/apollo-router/tests/snapshots/set_context__set_context_with_null.snap new file mode 100644 index 0000000000..1e361f0a83 --- /dev/null +++ b/apollo-router/tests/snapshots/set_context__set_context_with_null.snap @@ -0,0 +1,99 @@ +--- +source: apollo-router/tests/set_context.rs +expression: response +--- +{ + "data": { + "t": { + "id": "1", + "u": { + "field": 1234 + } + } + }, + "extensions": { + "apolloQueryPlan": { + "object": { + "kind": "QueryPlan", + "node": { + "kind": "Sequence", + "nodes": [ + { + "kind": "Fetch", + "serviceName": "Subgraph1", + "variableUsages": [], + "operation": "query Query_Null_Param__Subgraph1__0{t{__typename prop id u{__typename id}}}", + "operationName": "Query_Null_Param__Subgraph1__0", + "operationKind": "query", + "id": null, + "inputRewrites": null, + "outputRewrites": null, + "contextRewrites": null, + "schemaAwareHash": "19bd66a3ecc2d9495dffce2279774de3275cb027254289bb61b0c1937a7738b4", + "authorization": { + "is_authenticated": false, + "scopes": [], + "policies": [] + } + }, + { + "kind": "Flatten", + "path": [ + "", + "t", + "u" + ], + "node": { + "kind": "Fetch", + "serviceName": "Subgraph1", + "requires": [ + { + "kind": "InlineFragment", + "typeCondition": "U", + "selections": [ + { + "kind": "Field", + "name": "__typename" + }, + { + "kind": "Field", + "name": "id" + } + ] + } + ], + "variableUsages": [ + "contextualArgument_1_0" + ], + "operation": "query Query_Null_Param__Subgraph1__1($representations:[_Any!]!$contextualArgument_1_0:String){_entities(representations:$representations){...on U{field(a:$contextualArgument_1_0)}}}", + "operationName": "Query_Null_Param__Subgraph1__1", + "operationKind": "query", + "id": null, + "inputRewrites": null, + "outputRewrites": null, + "contextRewrites": [ + { + "kind": "KeyRenamer", + "path": [ + "..", + "... on T", + "prop" + ], + "renameKeyTo": "contextualArgument_1_0" + } + ], + "schemaAwareHash": "010ba25ca76f881bd9f0d5e338f9c07829d4d00e183828b6577d593aea0cf21e", + "authorization": { + "is_authenticated": false, + "scopes": [], + "policies": [] + } + } + } + ] + } + }, + "text": "QueryPlan {\n Sequence {\n Fetch(service: \"Subgraph1\") {\n {\n t {\n __typename\n prop\n id\n u {\n __typename\n id\n }\n }\n }\n },\n Flatten(path: \".t.u\") {\n Fetch(service: \"Subgraph1\") {\n {\n ... on U {\n __typename\n id\n }\n } =>\n {\n ... on U {\n field(a: $contextualArgument_1_0)\n }\n }\n },\n },\n },\n}" + } + } +} diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index d36d923c6d..0708d9b467 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -20,7 +20,7 @@ reqwest = { workspace = true, features = ["json", "blocking"] } serde_json.workspace = true tokio.workspace = true # note: this dependency should _always_ be pinned, prefix the version with an `=` -router-bridge = { git = "https://github.com/apollographql/federation-rs", rev = "ced6eed5c4394bb3cbf3867798b8b7f63cb82d66" } +router-bridge = "=0.5.23+v2.8.0-alpha.0" [dev-dependencies] anyhow = "1" From d99229ec1e6f54d7b9fd26c94662556677302101 Mon Sep 17 00:00:00 2001 From: Chris Lenfest Date: Fri, 17 May 2024 01:03:42 -0500 Subject: [PATCH 41/69] add a couple of tests --- .../src/query_planner/subgraph_context.rs | 80 +++++++--- .../tests/fixtures/set_context/one.json | 39 +++++ apollo-router/tests/set_context.rs | 26 ++++ ...ontext__set_context_list_of_lists.snap.new | 138 ------------------ ...et_context__set_context_type_mismatch.snap | 99 +++++++++++++ 5 files changed, 226 insertions(+), 156 deletions(-) delete mode 100644 apollo-router/tests/snapshots/set_context__set_context_list_of_lists.snap.new create mode 100644 apollo-router/tests/snapshots/set_context__set_context_type_mismatch.snap diff --git a/apollo-router/src/query_planner/subgraph_context.rs b/apollo-router/src/query_planner/subgraph_context.rs index d6e7902bc1..0191c02632 100644 --- a/apollo-router/src/query_planner/subgraph_context.rs +++ b/apollo-router/src/query_planner/subgraph_context.rs @@ -350,24 +350,68 @@ mod subgraph_context_unit_tests { }, } } + + #[test] + fn test_transform_selection_set() { + let type_name = executable::Name::new("Hello").unwrap(); + let field_name = executable::Name::new("f").unwrap(); + let field_definition = ast::FieldDefinition { + description: None, + name: field_name.clone(), + arguments: vec![ + Node::new(ast::InputValueDefinition { + description: None, + name: executable::Name::new("param").unwrap(), + ty: Node::new(ast::Type::Named(executable::Name::new("ParamType").unwrap())), + default_value: None, + directives: ast::DirectiveList(vec![]), + }), + ], + ty: ast::Type::Named(executable::Name::new("FieldType").unwrap()), + directives: ast::DirectiveList(vec![]), + }; + let mut selection_set = SelectionSet::new(type_name); + let field = executable::Field::new( + executable::Name::new("f").unwrap(), + Node::new(field_definition), + ).with_argument(executable::Name::new("param").unwrap(), Node::new(ast::Value::Variable(executable::Name::new("variable").unwrap()))); + + selection_set.push(Selection::Field(Node::new(field))); + + // before modifications + assert_eq!( + "{ f(param: $variable) }", + selection_set.serialize().no_indent().to_string() + ); - // #[test] - // fn test_merge_context_path() { - // let old_query = "query QueryLL__Subgraph1__1($representations:[_Any!]!$Subgraph1_U_field_a:String!){_entities(representations:$representations){...on U{id field(a:$Subgraph1_U_field_a)}}}"; - // let mut ctx_args = HashSet::new(); - // ctx_args.insert("Subgraph1_U_field_a".to_string()); - // let contextual_args = Some((ctx_args, 2)); - - // let expected = "query QueryLL__Subgraph1__1($representations: [_Any!]!, $Subgraph1_U_field_a_0: String!, $Subgraph1_U_field_a_1: String!) { _0: _entities(representations: $representations) { ... on U { id field(a: $Subgraph1_U_field_a_0) } } _1: _entities(representations: $representations) { ... on U { id field(a: $Subgraph1_U_field_a_1) } } }"; + let mut hash_set = HashSet::new(); + + // create a hash set that will miss completely. transform has no effect + hash_set.insert("one".to_string()); + hash_set.insert("two".to_string()); + hash_set.insert("param".to_string()); + let mut clone = selection_set.clone(); + transform_selection_set(&mut clone, &hash_set, 7, false); + assert_eq!( + "{ f(param: $variable) }", + clone.serialize().no_indent().to_string() + ); + + // add variable that will hit and cause a rewrite + hash_set.insert("variable".to_string()); + let mut clone = selection_set.clone(); + transform_selection_set(&mut clone, &hash_set, 7, false); + assert_eq!( + "{ f(param: $variable_7) }", + clone.serialize().no_indent().to_string() + ); - // assert_eq!( - // expected, - // build_operation_with_aliasing(old_query, &contextual_args) - // .unwrap() - // .serialize() - // .no_indent() - // .to_string() - // .as_str() - // ); - // } + // add_alias = true will add a "_3:" alias + let mut clone = selection_set.clone(); + transform_selection_set(&mut clone, &hash_set, 3, true); + assert_eq!( + "{ _3: f(param: $variable_3) }", + clone.serialize().no_indent().to_string() + ); + } } diff --git a/apollo-router/tests/fixtures/set_context/one.json b/apollo-router/tests/fixtures/set_context/one.json index 1245129c99..9d56598722 100644 --- a/apollo-router/tests/fixtures/set_context/one.json +++ b/apollo-router/tests/fixtures/set_context/one.json @@ -279,6 +279,45 @@ ] } } + }, + { + "request": { + "query": "query Query_type_mismatch__Subgraph1__0{t{__typename prop id u{__typename id}}}", + "operationName": "Query_type_mismatch__Subgraph1__0" + }, + "response": { + "data": { + "t": { + "__typename": "T", + "prop": 7, + "id": "1", + "u": { + "__typename": "U", + "id": "1" + } + } + } + } + }, + { + "request": { + "query": "query Query_type_mismatch__Subgraph1__1($representations:[_Any!]!$contextualArgument_1_0:String){_entities(representations:$representations){...on U{field(a:$contextualArgument_1_0)}}}", + "operationName": "Query_type_mismatch__Subgraph1__1", + "variables": { + "contextualArgument_1_0": 7, + "representations": [{ "__typename": "U", "id": "1" }] + } + }, + "response": { + "data": { + "_entities": [ + { + "id": "1", + "field": 1234 + } + ] + } + } } ] } diff --git a/apollo-router/tests/set_context.rs b/apollo-router/tests/set_context.rs index fb1688ead7..f17c1505eb 100644 --- a/apollo-router/tests/set_context.rs +++ b/apollo-router/tests/set_context.rs @@ -206,6 +206,32 @@ async fn test_set_context_with_null() { insta::assert_json_snapshot!(response); } +// this test returns the contextual value with a different than expected type +// this currently works, but perhaps should do type valdiation in the future to reject +#[tokio::test(flavor = "multi_thread")] +async fn test_set_context_type_mismatch() { + static QUERY: &str = r#" + query Query_type_mismatch { + t { + id + u { + field + } + } + }"#; + + let response = run_single_request( + QUERY, + &[ + ("Subgraph1", include_str!("fixtures/set_context/one.json")), + ("Subgraph2", include_str!("fixtures/set_context/two.json")), + ], + ) + .await; + + insta::assert_json_snapshot!(response); +} + fn setup_from_mocks( configuration: serde_json::Value, mocks: &[(&'static str, &'static str)], diff --git a/apollo-router/tests/snapshots/set_context__set_context_list_of_lists.snap.new b/apollo-router/tests/snapshots/set_context__set_context_list_of_lists.snap.new deleted file mode 100644 index 806ea05aff..0000000000 --- a/apollo-router/tests/snapshots/set_context__set_context_list_of_lists.snap.new +++ /dev/null @@ -1,138 +0,0 @@ ---- -source: apollo-router/tests/set_context.rs -assertion_line: 152 -expression: response ---- -{ - "data": { - "tList": [ - { - "id": "1", - "uList": [ - null - ] - }, - { - "id": "2", - "uList": [ - null - ] - } - ] - }, - "errors": [ - { - "message": "couldn't find mock for query {\"query\":\"query QueryLL__Subgraph1__1($representations:[_Any!]!$contextualArgument_1_0:String){_entities(representations:$representations){...on U{field(a:$contextualArgument_1_0)}}}\",\"operationName\":\"QueryLL__Subgraph1__1\",\"variables\":{\"contextualArgument_1_0_0\":\"prop value 1\",\"contextualArgument_1_0_1\":\"prop value 2\",\"representations\":[{\"__typename\":\"U\",\"id\":\"3\"},{\"__typename\":\"U\",\"id\":\"4\"}]}}", - "extensions": { - "code": "FETCH_ERROR" - } - } - ], - "extensions": { - "valueCompletion": [ - { - "message": "Cannot return null for non-nullable field U.field", - "path": [ - "tList", - 0, - "uList", - 0 - ] - }, - { - "message": "Cannot return null for non-nullable field U.field", - "path": [ - "tList", - 1, - "uList", - 0 - ] - } - ], - "apolloQueryPlan": { - "object": { - "kind": "QueryPlan", - "node": { - "kind": "Sequence", - "nodes": [ - { - "kind": "Fetch", - "serviceName": "Subgraph1", - "variableUsages": [], - "operation": "query QueryLL__Subgraph1__0{tList{__typename prop id uList{__typename id}}}", - "operationName": "QueryLL__Subgraph1__0", - "operationKind": "query", - "id": null, - "inputRewrites": null, - "outputRewrites": null, - "contextRewrites": null, - "schemaAwareHash": "babf88ea82c1330e535966572a55b03a2934097cd1cf905303b86ae7c197ccaf", - "authorization": { - "is_authenticated": false, - "scopes": [], - "policies": [] - } - }, - { - "kind": "Flatten", - "path": [ - "", - "tList", - "@", - "uList", - "@" - ], - "node": { - "kind": "Fetch", - "serviceName": "Subgraph1", - "requires": [ - { - "kind": "InlineFragment", - "typeCondition": "U", - "selections": [ - { - "kind": "Field", - "name": "__typename" - }, - { - "kind": "Field", - "name": "id" - } - ] - } - ], - "variableUsages": [ - "contextualArgument_1_0" - ], - "operation": "query QueryLL__Subgraph1__1($representations:[_Any!]!$contextualArgument_1_0:String){_entities(representations:$representations){...on U{field(a:$contextualArgument_1_0)}}}", - "operationName": "QueryLL__Subgraph1__1", - "operationKind": "query", - "id": null, - "inputRewrites": null, - "outputRewrites": null, - "contextRewrites": [ - { - "kind": "KeyRenamer", - "path": [ - "..", - "... on T", - "prop" - ], - "renameKeyTo": "contextualArgument_1_0" - } - ], - "schemaAwareHash": "a9b24549250c12e38c398c32e9218134fab000be3b934ebc6bb38ea096343646", - "authorization": { - "is_authenticated": false, - "scopes": [], - "policies": [] - } - } - } - ] - } - }, - "text": "QueryPlan {\n Sequence {\n Fetch(service: \"Subgraph1\") {\n {\n tList {\n __typename\n prop\n id\n uList {\n __typename\n id\n }\n }\n }\n },\n Flatten(path: \".tList.@.uList.@\") {\n Fetch(service: \"Subgraph1\") {\n {\n ... on U {\n __typename\n id\n }\n } =>\n {\n ... on U {\n field(a: $contextualArgument_1_0)\n }\n }\n },\n },\n },\n}" - } - } -} diff --git a/apollo-router/tests/snapshots/set_context__set_context_type_mismatch.snap b/apollo-router/tests/snapshots/set_context__set_context_type_mismatch.snap new file mode 100644 index 0000000000..ac7659fd4b --- /dev/null +++ b/apollo-router/tests/snapshots/set_context__set_context_type_mismatch.snap @@ -0,0 +1,99 @@ +--- +source: apollo-router/tests/set_context.rs +expression: response +--- +{ + "data": { + "t": { + "id": "1", + "u": { + "field": 1234 + } + } + }, + "extensions": { + "apolloQueryPlan": { + "object": { + "kind": "QueryPlan", + "node": { + "kind": "Sequence", + "nodes": [ + { + "kind": "Fetch", + "serviceName": "Subgraph1", + "variableUsages": [], + "operation": "query Query_type_mismatch__Subgraph1__0{t{__typename prop id u{__typename id}}}", + "operationName": "Query_type_mismatch__Subgraph1__0", + "operationKind": "query", + "id": null, + "inputRewrites": null, + "outputRewrites": null, + "contextRewrites": null, + "schemaAwareHash": "7eae890e61f5ae512e112f5260abe0de3504041c92dbcc7aae0891c9bdf2222b", + "authorization": { + "is_authenticated": false, + "scopes": [], + "policies": [] + } + }, + { + "kind": "Flatten", + "path": [ + "", + "t", + "u" + ], + "node": { + "kind": "Fetch", + "serviceName": "Subgraph1", + "requires": [ + { + "kind": "InlineFragment", + "typeCondition": "U", + "selections": [ + { + "kind": "Field", + "name": "__typename" + }, + { + "kind": "Field", + "name": "id" + } + ] + } + ], + "variableUsages": [ + "contextualArgument_1_0" + ], + "operation": "query Query_type_mismatch__Subgraph1__1($representations:[_Any!]!$contextualArgument_1_0:String){_entities(representations:$representations){...on U{field(a:$contextualArgument_1_0)}}}", + "operationName": "Query_type_mismatch__Subgraph1__1", + "operationKind": "query", + "id": null, + "inputRewrites": null, + "outputRewrites": null, + "contextRewrites": [ + { + "kind": "KeyRenamer", + "path": [ + "..", + "... on T", + "prop" + ], + "renameKeyTo": "contextualArgument_1_0" + } + ], + "schemaAwareHash": "d8ea99348ab32931371c85c09565cfb728d2e48cf017201cd79cb9ef860eb9c2", + "authorization": { + "is_authenticated": false, + "scopes": [], + "policies": [] + } + } + } + ] + } + }, + "text": "QueryPlan {\n Sequence {\n Fetch(service: \"Subgraph1\") {\n {\n t {\n __typename\n prop\n id\n u {\n __typename\n id\n }\n }\n }\n },\n Flatten(path: \".t.u\") {\n Fetch(service: \"Subgraph1\") {\n {\n ... on U {\n __typename\n id\n }\n } =>\n {\n ... on U {\n field(a: $contextualArgument_1_0)\n }\n }\n },\n },\n },\n}" + } + } +} From b848d224155462f6b6665f6a516fff4ad5b27777 Mon Sep 17 00:00:00 2001 From: o0Ignition0o Date: Fri, 17 May 2024 15:10:26 +0200 Subject: [PATCH 42/69] validate subgraph operations + update snapshot ordering --- apollo-router/src/query_planner/execution.rs | 7 + apollo-router/src/query_planner/fetch.rs | 33 ++-- .../src/query_planner/subgraph_context.rs | 59 +++++--- apollo-router/src/query_planner/tests.rs | 8 + .../src/services/execution/service.rs | 3 + .../tests/fixtures/set_context/one.json | 2 +- apollo-router/tests/set_context.rs | 23 ++- .../snapshots/set_context__set_context.snap | 88 +++++------ .../set_context__set_context_list.snap | 90 +++++------ ...et_context__set_context_list_of_lists.snap | 92 ++++++------ ...set_context__set_context_no_typenames.snap | 88 +++++------ ...et_context__set_context_type_mismatch.snap | 88 +++++------ .../set_context__set_context_union.snap | 142 +++++++++--------- 13 files changed, 385 insertions(+), 338 deletions(-) diff --git a/apollo-router/src/query_planner/execution.rs b/apollo-router/src/query_planner/execution.rs index 801069afd1..dc1e27123e 100644 --- a/apollo-router/src/query_planner/execution.rs +++ b/apollo-router/src/query_planner/execution.rs @@ -1,6 +1,7 @@ use std::collections::HashMap; use std::sync::Arc; +use apollo_compiler::validation::Valid; use apollo_compiler::NodeStr; use futures::future::join_all; use futures::prelude::*; @@ -50,6 +51,7 @@ impl QueryPlan { service_factory: &'a Arc, supergraph_request: &'a Arc>, schema: &'a Arc, + subgraph_schemas: &'a Arc>>>, sender: mpsc::Sender, subscription_handle: Option, subscription_config: &'a Option, @@ -73,6 +75,7 @@ impl QueryPlan { root_node: &self.root, subscription_handle: &subscription_handle, subscription_config, + subgraph_schemas, }, &root, &initial_value.unwrap_or_default(), @@ -100,6 +103,7 @@ pub(crate) struct ExecutionParameters<'a> { pub(crate) context: &'a Context, pub(crate) service_factory: &'a Arc, pub(crate) schema: &'a Arc, + pub(crate) subgraph_schemas: &'a Arc>>>, pub(crate) supergraph_request: &'a Arc>, pub(crate) deferred_fetches: &'a HashMap)>>, pub(crate) query: &'a Arc, @@ -293,6 +297,7 @@ impl PlanNode { root_node: parameters.root_node, subscription_handle: parameters.subscription_handle, subscription_config: parameters.subscription_config, + subgraph_schemas: parameters.subgraph_schemas, }, current_dir, &value, @@ -435,6 +440,7 @@ impl DeferredNode { let label = self.label.as_ref().map(|l| l.to_string()); let tx = sender; let sc = parameters.schema.clone(); + let subgraph_schemas = parameters.subgraph_schemas.clone(); let orig = parameters.supergraph_request.clone(); let sf = parameters.service_factory.clone(); let root_node = parameters.root_node.clone(); @@ -481,6 +487,7 @@ impl DeferredNode { root_node: &root_node, subscription_handle: &subscription_handle, subscription_config: &subscription_config, + subgraph_schemas: &subgraph_schemas, }, &Path::default(), &value, diff --git a/apollo-router/src/query_planner/fetch.rs b/apollo-router/src/query_planner/fetch.rs index 1d27725d08..87c46b8bdb 100644 --- a/apollo-router/src/query_planner/fetch.rs +++ b/apollo-router/src/query_planner/fetch.rs @@ -396,18 +396,29 @@ impl FetchNode { let alias_query_string; // this exists outside the if block to allow the as_str() to be longer lived let aliased_operation = if let Some(ctx_arg) = contextual_arguments { - match build_operation_with_aliasing(operation, &ctx_arg, parameters.schema) { - Ok(op) => { - alias_query_string = op.serialize().no_indent().to_string(); - alias_query_string.as_str() - } - Err(errors) => { - tracing::debug!( - "couldn't generate a valid executable document? {:?}", - errors - ); - operation.as_serialized() + // TODO: subgraph_schemas should be a HashMap of NodeStr or Name. + if let Some(subgraph_schema) = + parameters.subgraph_schemas.get(&service_name.to_string()) + { + match build_operation_with_aliasing(operation, &ctx_arg, subgraph_schema) { + Ok(op) => { + alias_query_string = op.serialize().no_indent().to_string(); + alias_query_string.as_str() + } + Err(errors) => { + tracing::debug!( + "couldn't generate a valid executable document? {:?}", + errors + ); + operation.as_serialized() + } } + } else { + tracing::debug!( + "couldn't find a subgraph schema for service {:?}", + &service_name + ); + operation.as_serialized() } } else { operation.as_serialized() diff --git a/apollo-router/src/query_planner/subgraph_context.rs b/apollo-router/src/query_planner/subgraph_context.rs index 0191c02632..28849c4c3c 100644 --- a/apollo-router/src/query_planner/subgraph_context.rs +++ b/apollo-router/src/query_planner/subgraph_context.rs @@ -206,12 +206,11 @@ impl<'a> SubgraphContext<'a> { pub(crate) fn build_operation_with_aliasing( subgraph_operation: &SubgraphOperation, contextual_arguments: &ContextualArguments, - schema: &Schema, + subgraph_schema: &Valid, ) -> Result, ContextBatchingError> { let mut selections: Vec = vec![]; let ContextualArguments { arguments, count } = contextual_arguments; - let parsed_document = - subgraph_operation.as_parsed(schema.federation_supergraph().schema.schema()); + let parsed_document = subgraph_operation.as_parsed(subgraph_schema); if let Ok(document) = parsed_document { // TODO: Can there be more than one named operation? // Can there be an anonymous operation? @@ -237,7 +236,13 @@ pub(crate) fn build_operation_with_aliasing( // it is a field selection for _entities, so it's ok to reach in and give it an alias let mut selection_set = op.selection_set.clone(); transform_selection_set(&mut selection_set, arguments, i, true); - selections.push(selection_set.selections[0].clone()) + selection_set.selections.get_mut(0).map(|selection| { + if let Selection::Field(f) = selection { + let field = f.make_mut(); + field.alias = Some(Name::new_unchecked(format!("_{}", i).into())); + } + selections.push(selection.clone()); + }); } let mut ed = ExecutableDocument::new(); @@ -252,11 +257,9 @@ pub(crate) fn build_operation_with_aliasing( }, }); - let valid_document = ed - .validate(schema.federation_supergraph().schema.schema()) - .map_err(ContextBatchingError::InvalidDocumentGenerated)?; - - return Ok(valid_document); + return ed + .validate(subgraph_schema) + .map_err(ContextBatchingError::InvalidDocumentGenerated); } } Err(ContextBatchingError::NoSelectionSet) @@ -334,7 +337,7 @@ mod subgraph_context_unit_tests { let result = merge_context_path(¤t_dir, &relative_path).unwrap(); assert_eq!(expected, serde_json::to_string(&result).unwrap(),); } - + #[test] fn test_merge_context_path_invalid() { let current_dir: Path = serde_json::from_str(r#"["t","u"]"#).unwrap(); @@ -350,7 +353,7 @@ mod subgraph_context_unit_tests { }, } } - + #[test] fn test_transform_selection_set() { let type_name = executable::Name::new("Hello").unwrap(); @@ -358,15 +361,15 @@ mod subgraph_context_unit_tests { let field_definition = ast::FieldDefinition { description: None, name: field_name.clone(), - arguments: vec![ - Node::new(ast::InputValueDefinition { - description: None, - name: executable::Name::new("param").unwrap(), - ty: Node::new(ast::Type::Named(executable::Name::new("ParamType").unwrap())), - default_value: None, - directives: ast::DirectiveList(vec![]), - }), - ], + arguments: vec![Node::new(ast::InputValueDefinition { + description: None, + name: executable::Name::new("param").unwrap(), + ty: Node::new(ast::Type::Named( + executable::Name::new("ParamType").unwrap(), + )), + default_value: None, + directives: ast::DirectiveList(vec![]), + })], ty: ast::Type::Named(executable::Name::new("FieldType").unwrap()), directives: ast::DirectiveList(vec![]), }; @@ -374,10 +377,16 @@ mod subgraph_context_unit_tests { let field = executable::Field::new( executable::Name::new("f").unwrap(), Node::new(field_definition), - ).with_argument(executable::Name::new("param").unwrap(), Node::new(ast::Value::Variable(executable::Name::new("variable").unwrap()))); - + ) + .with_argument( + executable::Name::new("param").unwrap(), + Node::new(ast::Value::Variable( + executable::Name::new("variable").unwrap(), + )), + ); + selection_set.push(Selection::Field(Node::new(field))); - + // before modifications assert_eq!( "{ f(param: $variable) }", @@ -385,7 +394,7 @@ mod subgraph_context_unit_tests { ); let mut hash_set = HashSet::new(); - + // create a hash set that will miss completely. transform has no effect hash_set.insert("one".to_string()); hash_set.insert("two".to_string()); @@ -396,7 +405,7 @@ mod subgraph_context_unit_tests { "{ f(param: $variable) }", clone.serialize().no_indent().to_string() ); - + // add variable that will hit and cause a rewrite hash_set.insert("variable".to_string()); let mut clone = selection_set.clone(); diff --git a/apollo-router/src/query_planner/tests.rs b/apollo-router/src/query_planner/tests.rs index 62f30f20a1..15f2d3b421 100644 --- a/apollo-router/src/query_planner/tests.rs +++ b/apollo-router/src/query_planner/tests.rs @@ -116,6 +116,7 @@ async fn mock_subgraph_service_withf_panics_should_be_reported_as_service_closed &sf, &Default::default(), &Arc::new(Schema::parse_test(test_schema!(), &Default::default()).unwrap()), + &Default::default(), sender, None, &None, @@ -177,6 +178,7 @@ async fn fetch_includes_operation_name() { &sf, &Default::default(), &Arc::new(Schema::parse_test(test_schema!(), &Default::default()).unwrap()), + &Default::default(), sender, None, &None, @@ -235,6 +237,7 @@ async fn fetch_makes_post_requests() { &sf, &Default::default(), &Arc::new(Schema::parse_test(test_schema!(), &Default::default()).unwrap()), + &Default::default(), sender, None, &None, @@ -387,6 +390,7 @@ async fn defer() { &sf, &Default::default(), &schema, + &Default::default(), sender, None, &None, @@ -495,6 +499,7 @@ async fn defer_if_condition() { .unwrap(), ), &schema, + &Default::default(), sender, None, &None, @@ -517,6 +522,7 @@ async fn defer_if_condition() { &service_factory, &Default::default(), &schema, + &Default::default(), default_sender, None, &None, @@ -548,6 +554,7 @@ async fn defer_if_condition() { .unwrap(), ), &schema, + &Default::default(), sender, None, &None, @@ -669,6 +676,7 @@ async fn dependent_mutations() { &sf, &Default::default(), &Arc::new(Schema::parse_test(schema, &Default::default()).unwrap()), + &Default::default(), sender, None, &None, diff --git a/apollo-router/src/services/execution/service.rs b/apollo-router/src/services/execution/service.rs index ddd9f96ca6..fbde3de97a 100644 --- a/apollo-router/src/services/execution/service.rs +++ b/apollo-router/src/services/execution/service.rs @@ -57,6 +57,7 @@ use crate::spec::Schema; #[derive(Clone)] pub(crate) struct ExecutionService { pub(crate) schema: Arc, + pub(crate) subgraph_schemas: Arc>>>, pub(crate) subgraph_service_factory: Arc, /// Subscription config if enabled subscription_config: Option, @@ -148,6 +149,7 @@ impl ExecutionService { &self.subgraph_service_factory, &Arc::new(req.supergraph_request), &self.schema, + &self.subgraph_schemas, sender, subscription_handle.clone(), &self.subscription_config, @@ -618,6 +620,7 @@ impl ServiceFactory for ExecutionServiceFactory { schema: self.schema.clone(), subgraph_service_factory: self.subgraph_service_factory.clone(), subscription_config: subscription_plugin_conf, + subgraph_schemas: self.subgraph_schemas.clone(), } .boxed(), |acc, (_, e)| e.execution_service(acc), diff --git a/apollo-router/tests/fixtures/set_context/one.json b/apollo-router/tests/fixtures/set_context/one.json index 9d56598722..0e2dfecfec 100644 --- a/apollo-router/tests/fixtures/set_context/one.json +++ b/apollo-router/tests/fixtures/set_context/one.json @@ -198,8 +198,8 @@ "query": "query QueryLL__Subgraph1__1($representations: [_Any!]!, $contextualArgument_1_0_0: String, $contextualArgument_1_0_1: String) { _0: _entities(representations: $representations) { ... on U { field(a: $contextualArgument_1_0_0) } } _1: _entities(representations: $representations) { ... on U { field(a: $contextualArgument_1_0_1) } } }", "operationName": "QueryLL__Subgraph1__1", "variables": { - "contextualArgument_1_0_1": "prop value 2", "contextualArgument_1_0_0": "prop value 1", + "contextualArgument_1_0_1": "prop value 2", "representations": [ { "__typename": "U", "id": "3" }, { "__typename": "U", "id": "4" } diff --git a/apollo-router/tests/set_context.rs b/apollo-router/tests/set_context.rs index f17c1505eb..ccb790c678 100644 --- a/apollo-router/tests/set_context.rs +++ b/apollo-router/tests/set_context.rs @@ -23,6 +23,15 @@ struct RequestAndResponse { response: Response, } +macro_rules! snap +{ + ($result:ident) => { + insta::with_settings!({sort_maps => true}, { + insta::assert_json_snapshot!($result); + }); + } +} + async fn run_single_request(query: &str, mocks: &[(&'static str, &'static str)]) -> Response { let harness = setup_from_mocks( json! {{ @@ -77,7 +86,7 @@ async fn test_set_context() { ) .await; - insta::assert_json_snapshot!(response); + snap!(response); } #[tokio::test(flavor = "multi_thread")] @@ -101,7 +110,7 @@ async fn test_set_context_no_typenames() { ) .await; - insta::assert_json_snapshot!(response); + snap!(response); } #[tokio::test(flavor = "multi_thread")] @@ -125,7 +134,7 @@ async fn test_set_context_list() { ) .await; - insta::assert_json_snapshot!(response); + snap!(response); } #[tokio::test(flavor = "multi_thread")] @@ -149,7 +158,7 @@ async fn test_set_context_list_of_lists() { ) .await; - insta::assert_json_snapshot!(response); + snap!(response); } #[tokio::test(flavor = "multi_thread")] @@ -179,7 +188,7 @@ async fn test_set_context_union() { ) .await; - insta::assert_json_snapshot!(response); + snap!(response); } #[tokio::test(flavor = "multi_thread")] @@ -219,7 +228,7 @@ async fn test_set_context_type_mismatch() { } } }"#; - + let response = run_single_request( QUERY, &[ @@ -229,7 +238,7 @@ async fn test_set_context_type_mismatch() { ) .await; - insta::assert_json_snapshot!(response); + snap!(response); } fn setup_from_mocks( diff --git a/apollo-router/tests/snapshots/set_context__set_context.snap b/apollo-router/tests/snapshots/set_context__set_context.snap index 79c4e26501..2e11680753 100644 --- a/apollo-router/tests/snapshots/set_context__set_context.snap +++ b/apollo-router/tests/snapshots/set_context__set_context.snap @@ -21,37 +21,52 @@ expression: response "kind": "Sequence", "nodes": [ { + "authorization": { + "is_authenticated": false, + "policies": [], + "scopes": [] + }, + "contextRewrites": null, + "id": null, + "inputRewrites": null, "kind": "Fetch", - "serviceName": "Subgraph1", - "variableUsages": [], "operation": "query Query__Subgraph1__0{t{__typename prop id u{__typename id}}}", - "operationName": "Query__Subgraph1__0", "operationKind": "query", - "id": null, - "inputRewrites": null, + "operationName": "Query__Subgraph1__0", "outputRewrites": null, - "contextRewrites": null, "schemaAwareHash": "d7cb2d1809789d49360ca0a60570555f83855f00547675f366915c9d9d90fef9", - "authorization": { - "is_authenticated": false, - "scopes": [], - "policies": [] - } + "serviceName": "Subgraph1", + "variableUsages": [] }, { "kind": "Flatten", - "path": [ - "", - "t", - "u" - ], "node": { + "authorization": { + "is_authenticated": false, + "policies": [], + "scopes": [] + }, + "contextRewrites": [ + { + "kind": "KeyRenamer", + "path": [ + "..", + "... on T", + "prop" + ], + "renameKeyTo": "contextualArgument_1_0" + } + ], + "id": null, + "inputRewrites": null, "kind": "Fetch", - "serviceName": "Subgraph1", + "operation": "query Query__Subgraph1__1($representations:[_Any!]!$contextualArgument_1_0:String){_entities(representations:$representations){...on U{field(a:$contextualArgument_1_0)}}}", + "operationKind": "query", + "operationName": "Query__Subgraph1__1", + "outputRewrites": null, "requires": [ { "kind": "InlineFragment", - "typeCondition": "U", "selections": [ { "kind": "Field", @@ -61,36 +76,21 @@ expression: response "kind": "Field", "name": "id" } - ] - } - ], - "variableUsages": [ - "contextualArgument_1_0" - ], - "operation": "query Query__Subgraph1__1($representations:[_Any!]!$contextualArgument_1_0:String){_entities(representations:$representations){...on U{field(a:$contextualArgument_1_0)}}}", - "operationName": "Query__Subgraph1__1", - "operationKind": "query", - "id": null, - "inputRewrites": null, - "outputRewrites": null, - "contextRewrites": [ - { - "kind": "KeyRenamer", - "path": [ - "..", - "... on T", - "prop" ], - "renameKeyTo": "contextualArgument_1_0" + "typeCondition": "U" } ], "schemaAwareHash": "66b954f39aead8436321c671eb71e56ce15bbe0c7b82f06b2f8f70473ce1cb6e", - "authorization": { - "is_authenticated": false, - "scopes": [], - "policies": [] - } - } + "serviceName": "Subgraph1", + "variableUsages": [ + "contextualArgument_1_0" + ] + }, + "path": [ + "", + "t", + "u" + ] } ] } diff --git a/apollo-router/tests/snapshots/set_context__set_context_list.snap b/apollo-router/tests/snapshots/set_context__set_context_list.snap index b4f9c84d0c..095326167e 100644 --- a/apollo-router/tests/snapshots/set_context__set_context_list.snap +++ b/apollo-router/tests/snapshots/set_context__set_context_list.snap @@ -27,38 +27,52 @@ expression: response "kind": "Sequence", "nodes": [ { + "authorization": { + "is_authenticated": false, + "policies": [], + "scopes": [] + }, + "contextRewrites": null, + "id": null, + "inputRewrites": null, "kind": "Fetch", - "serviceName": "Subgraph1", - "variableUsages": [], "operation": "query Query__Subgraph1__0{t{__typename prop id uList{__typename id}}}", - "operationName": "Query__Subgraph1__0", "operationKind": "query", - "id": null, - "inputRewrites": null, + "operationName": "Query__Subgraph1__0", "outputRewrites": null, - "contextRewrites": null, "schemaAwareHash": "4f746b9319e3ca4f234269464b6815eb97782f2ffe36774b998e7fb78f30abef", - "authorization": { - "is_authenticated": false, - "scopes": [], - "policies": [] - } + "serviceName": "Subgraph1", + "variableUsages": [] }, { "kind": "Flatten", - "path": [ - "", - "t", - "uList", - "@" - ], "node": { + "authorization": { + "is_authenticated": false, + "policies": [], + "scopes": [] + }, + "contextRewrites": [ + { + "kind": "KeyRenamer", + "path": [ + "..", + "... on T", + "prop" + ], + "renameKeyTo": "contextualArgument_1_0" + } + ], + "id": null, + "inputRewrites": null, "kind": "Fetch", - "serviceName": "Subgraph1", + "operation": "query Query__Subgraph1__1($representations:[_Any!]!$contextualArgument_1_0:String){_entities(representations:$representations){...on U{field(a:$contextualArgument_1_0)}}}", + "operationKind": "query", + "operationName": "Query__Subgraph1__1", + "outputRewrites": null, "requires": [ { "kind": "InlineFragment", - "typeCondition": "U", "selections": [ { "kind": "Field", @@ -68,36 +82,22 @@ expression: response "kind": "Field", "name": "id" } - ] - } - ], - "variableUsages": [ - "contextualArgument_1_0" - ], - "operation": "query Query__Subgraph1__1($representations:[_Any!]!$contextualArgument_1_0:String){_entities(representations:$representations){...on U{field(a:$contextualArgument_1_0)}}}", - "operationName": "Query__Subgraph1__1", - "operationKind": "query", - "id": null, - "inputRewrites": null, - "outputRewrites": null, - "contextRewrites": [ - { - "kind": "KeyRenamer", - "path": [ - "..", - "... on T", - "prop" ], - "renameKeyTo": "contextualArgument_1_0" + "typeCondition": "U" } ], "schemaAwareHash": "66b954f39aead8436321c671eb71e56ce15bbe0c7b82f06b2f8f70473ce1cb6e", - "authorization": { - "is_authenticated": false, - "scopes": [], - "policies": [] - } - } + "serviceName": "Subgraph1", + "variableUsages": [ + "contextualArgument_1_0" + ] + }, + "path": [ + "", + "t", + "uList", + "@" + ] } ] } diff --git a/apollo-router/tests/snapshots/set_context__set_context_list_of_lists.snap b/apollo-router/tests/snapshots/set_context__set_context_list_of_lists.snap index a10f9a3646..e7fbee2a8b 100644 --- a/apollo-router/tests/snapshots/set_context__set_context_list_of_lists.snap +++ b/apollo-router/tests/snapshots/set_context__set_context_list_of_lists.snap @@ -31,39 +31,52 @@ expression: response "kind": "Sequence", "nodes": [ { + "authorization": { + "is_authenticated": false, + "policies": [], + "scopes": [] + }, + "contextRewrites": null, + "id": null, + "inputRewrites": null, "kind": "Fetch", - "serviceName": "Subgraph1", - "variableUsages": [], "operation": "query QueryLL__Subgraph1__0{tList{__typename prop id uList{__typename id}}}", - "operationName": "QueryLL__Subgraph1__0", "operationKind": "query", - "id": null, - "inputRewrites": null, + "operationName": "QueryLL__Subgraph1__0", "outputRewrites": null, - "contextRewrites": null, "schemaAwareHash": "babf88ea82c1330e535966572a55b03a2934097cd1cf905303b86ae7c197ccaf", - "authorization": { - "is_authenticated": false, - "scopes": [], - "policies": [] - } + "serviceName": "Subgraph1", + "variableUsages": [] }, { "kind": "Flatten", - "path": [ - "", - "tList", - "@", - "uList", - "@" - ], "node": { + "authorization": { + "is_authenticated": false, + "policies": [], + "scopes": [] + }, + "contextRewrites": [ + { + "kind": "KeyRenamer", + "path": [ + "..", + "... on T", + "prop" + ], + "renameKeyTo": "contextualArgument_1_0" + } + ], + "id": null, + "inputRewrites": null, "kind": "Fetch", - "serviceName": "Subgraph1", + "operation": "query QueryLL__Subgraph1__1($representations:[_Any!]!$contextualArgument_1_0:String){_entities(representations:$representations){...on U{field(a:$contextualArgument_1_0)}}}", + "operationKind": "query", + "operationName": "QueryLL__Subgraph1__1", + "outputRewrites": null, "requires": [ { "kind": "InlineFragment", - "typeCondition": "U", "selections": [ { "kind": "Field", @@ -73,36 +86,23 @@ expression: response "kind": "Field", "name": "id" } - ] - } - ], - "variableUsages": [ - "contextualArgument_1_0" - ], - "operation": "query QueryLL__Subgraph1__1($representations:[_Any!]!$contextualArgument_1_0:String){_entities(representations:$representations){...on U{field(a:$contextualArgument_1_0)}}}", - "operationName": "QueryLL__Subgraph1__1", - "operationKind": "query", - "id": null, - "inputRewrites": null, - "outputRewrites": null, - "contextRewrites": [ - { - "kind": "KeyRenamer", - "path": [ - "..", - "... on T", - "prop" ], - "renameKeyTo": "contextualArgument_1_0" + "typeCondition": "U" } ], "schemaAwareHash": "a9b24549250c12e38c398c32e9218134fab000be3b934ebc6bb38ea096343646", - "authorization": { - "is_authenticated": false, - "scopes": [], - "policies": [] - } - } + "serviceName": "Subgraph1", + "variableUsages": [ + "contextualArgument_1_0" + ] + }, + "path": [ + "", + "tList", + "@", + "uList", + "@" + ] } ] } diff --git a/apollo-router/tests/snapshots/set_context__set_context_no_typenames.snap b/apollo-router/tests/snapshots/set_context__set_context_no_typenames.snap index d1c7e137fd..8eaa5b0202 100644 --- a/apollo-router/tests/snapshots/set_context__set_context_no_typenames.snap +++ b/apollo-router/tests/snapshots/set_context__set_context_no_typenames.snap @@ -19,37 +19,52 @@ expression: response "kind": "Sequence", "nodes": [ { + "authorization": { + "is_authenticated": false, + "policies": [], + "scopes": [] + }, + "contextRewrites": null, + "id": null, + "inputRewrites": null, "kind": "Fetch", - "serviceName": "Subgraph1", - "variableUsages": [], "operation": "query Query__Subgraph1__0{t{__typename prop id u{__typename id}}}", - "operationName": "Query__Subgraph1__0", "operationKind": "query", - "id": null, - "inputRewrites": null, + "operationName": "Query__Subgraph1__0", "outputRewrites": null, - "contextRewrites": null, "schemaAwareHash": "d7cb2d1809789d49360ca0a60570555f83855f00547675f366915c9d9d90fef9", - "authorization": { - "is_authenticated": false, - "scopes": [], - "policies": [] - } + "serviceName": "Subgraph1", + "variableUsages": [] }, { "kind": "Flatten", - "path": [ - "", - "t", - "u" - ], "node": { + "authorization": { + "is_authenticated": false, + "policies": [], + "scopes": [] + }, + "contextRewrites": [ + { + "kind": "KeyRenamer", + "path": [ + "..", + "... on T", + "prop" + ], + "renameKeyTo": "contextualArgument_1_0" + } + ], + "id": null, + "inputRewrites": null, "kind": "Fetch", - "serviceName": "Subgraph1", + "operation": "query Query__Subgraph1__1($representations:[_Any!]!$contextualArgument_1_0:String){_entities(representations:$representations){...on U{field(a:$contextualArgument_1_0)}}}", + "operationKind": "query", + "operationName": "Query__Subgraph1__1", + "outputRewrites": null, "requires": [ { "kind": "InlineFragment", - "typeCondition": "U", "selections": [ { "kind": "Field", @@ -59,36 +74,21 @@ expression: response "kind": "Field", "name": "id" } - ] - } - ], - "variableUsages": [ - "contextualArgument_1_0" - ], - "operation": "query Query__Subgraph1__1($representations:[_Any!]!$contextualArgument_1_0:String){_entities(representations:$representations){...on U{field(a:$contextualArgument_1_0)}}}", - "operationName": "Query__Subgraph1__1", - "operationKind": "query", - "id": null, - "inputRewrites": null, - "outputRewrites": null, - "contextRewrites": [ - { - "kind": "KeyRenamer", - "path": [ - "..", - "... on T", - "prop" ], - "renameKeyTo": "contextualArgument_1_0" + "typeCondition": "U" } ], "schemaAwareHash": "66b954f39aead8436321c671eb71e56ce15bbe0c7b82f06b2f8f70473ce1cb6e", - "authorization": { - "is_authenticated": false, - "scopes": [], - "policies": [] - } - } + "serviceName": "Subgraph1", + "variableUsages": [ + "contextualArgument_1_0" + ] + }, + "path": [ + "", + "t", + "u" + ] } ] } diff --git a/apollo-router/tests/snapshots/set_context__set_context_type_mismatch.snap b/apollo-router/tests/snapshots/set_context__set_context_type_mismatch.snap index ac7659fd4b..1df052723e 100644 --- a/apollo-router/tests/snapshots/set_context__set_context_type_mismatch.snap +++ b/apollo-router/tests/snapshots/set_context__set_context_type_mismatch.snap @@ -19,37 +19,52 @@ expression: response "kind": "Sequence", "nodes": [ { + "authorization": { + "is_authenticated": false, + "policies": [], + "scopes": [] + }, + "contextRewrites": null, + "id": null, + "inputRewrites": null, "kind": "Fetch", - "serviceName": "Subgraph1", - "variableUsages": [], "operation": "query Query_type_mismatch__Subgraph1__0{t{__typename prop id u{__typename id}}}", - "operationName": "Query_type_mismatch__Subgraph1__0", "operationKind": "query", - "id": null, - "inputRewrites": null, + "operationName": "Query_type_mismatch__Subgraph1__0", "outputRewrites": null, - "contextRewrites": null, "schemaAwareHash": "7eae890e61f5ae512e112f5260abe0de3504041c92dbcc7aae0891c9bdf2222b", - "authorization": { - "is_authenticated": false, - "scopes": [], - "policies": [] - } + "serviceName": "Subgraph1", + "variableUsages": [] }, { "kind": "Flatten", - "path": [ - "", - "t", - "u" - ], "node": { + "authorization": { + "is_authenticated": false, + "policies": [], + "scopes": [] + }, + "contextRewrites": [ + { + "kind": "KeyRenamer", + "path": [ + "..", + "... on T", + "prop" + ], + "renameKeyTo": "contextualArgument_1_0" + } + ], + "id": null, + "inputRewrites": null, "kind": "Fetch", - "serviceName": "Subgraph1", + "operation": "query Query_type_mismatch__Subgraph1__1($representations:[_Any!]!$contextualArgument_1_0:String){_entities(representations:$representations){...on U{field(a:$contextualArgument_1_0)}}}", + "operationKind": "query", + "operationName": "Query_type_mismatch__Subgraph1__1", + "outputRewrites": null, "requires": [ { "kind": "InlineFragment", - "typeCondition": "U", "selections": [ { "kind": "Field", @@ -59,36 +74,21 @@ expression: response "kind": "Field", "name": "id" } - ] - } - ], - "variableUsages": [ - "contextualArgument_1_0" - ], - "operation": "query Query_type_mismatch__Subgraph1__1($representations:[_Any!]!$contextualArgument_1_0:String){_entities(representations:$representations){...on U{field(a:$contextualArgument_1_0)}}}", - "operationName": "Query_type_mismatch__Subgraph1__1", - "operationKind": "query", - "id": null, - "inputRewrites": null, - "outputRewrites": null, - "contextRewrites": [ - { - "kind": "KeyRenamer", - "path": [ - "..", - "... on T", - "prop" ], - "renameKeyTo": "contextualArgument_1_0" + "typeCondition": "U" } ], "schemaAwareHash": "d8ea99348ab32931371c85c09565cfb728d2e48cf017201cd79cb9ef860eb9c2", - "authorization": { - "is_authenticated": false, - "scopes": [], - "policies": [] - } - } + "serviceName": "Subgraph1", + "variableUsages": [ + "contextualArgument_1_0" + ] + }, + "path": [ + "", + "t", + "u" + ] } ] } diff --git a/apollo-router/tests/snapshots/set_context__set_context_union.snap b/apollo-router/tests/snapshots/set_context__set_context_union.snap index 29df9c9a5e..e382988a8b 100644 --- a/apollo-router/tests/snapshots/set_context__set_context_union.snap +++ b/apollo-router/tests/snapshots/set_context__set_context_union.snap @@ -18,40 +18,55 @@ expression: response "kind": "Sequence", "nodes": [ { + "authorization": { + "is_authenticated": false, + "policies": [], + "scopes": [] + }, + "contextRewrites": null, + "id": null, + "inputRewrites": null, "kind": "Fetch", - "serviceName": "Subgraph1", - "variableUsages": [], "operation": "query QueryUnion__Subgraph1__0{k{__typename ...on A{__typename prop v{__typename id}}...on B{__typename prop v{__typename id}}}}", - "operationName": "QueryUnion__Subgraph1__0", "operationKind": "query", - "id": null, - "inputRewrites": null, + "operationName": "QueryUnion__Subgraph1__0", "outputRewrites": null, - "contextRewrites": null, "schemaAwareHash": "b9124cd1daa6e8347175ffe2108670a31c73cbc983e7812ee39f415235541005", - "authorization": { - "is_authenticated": false, - "scopes": [], - "policies": [] - } + "serviceName": "Subgraph1", + "variableUsages": [] }, { "kind": "Parallel", "nodes": [ { "kind": "Flatten", - "path": [ - "", - "k|[A]", - "v" - ], "node": { + "authorization": { + "is_authenticated": false, + "policies": [], + "scopes": [] + }, + "contextRewrites": [ + { + "kind": "KeyRenamer", + "path": [ + "..", + "... on A", + "prop" + ], + "renameKeyTo": "contextualArgument_1_1" + } + ], + "id": null, + "inputRewrites": null, "kind": "Fetch", - "serviceName": "Subgraph1", + "operation": "query QueryUnion__Subgraph1__1($representations:[_Any!]!$contextualArgument_1_1:String){_entities(representations:$representations){...on V{field(a:$contextualArgument_1_1)}}}", + "operationKind": "query", + "operationName": "QueryUnion__Subgraph1__1", + "outputRewrites": null, "requires": [ { "kind": "InlineFragment", - "typeCondition": "V", "selections": [ { "kind": "Field", @@ -61,51 +76,51 @@ expression: response "kind": "Field", "name": "id" } - ] + ], + "typeCondition": "V" } ], + "schemaAwareHash": "c50ca82d402a330c1b35a6d76332094c40b00d6dec6f6b2a9b0a32ced68f4e95", + "serviceName": "Subgraph1", "variableUsages": [ "contextualArgument_1_1" - ], - "operation": "query QueryUnion__Subgraph1__1($representations:[_Any!]!$contextualArgument_1_1:String){_entities(representations:$representations){...on V{field(a:$contextualArgument_1_1)}}}", - "operationName": "QueryUnion__Subgraph1__1", - "operationKind": "query", - "id": null, - "inputRewrites": null, - "outputRewrites": null, + ] + }, + "path": [ + "", + "k|[A]", + "v" + ] + }, + { + "kind": "Flatten", + "node": { + "authorization": { + "is_authenticated": false, + "policies": [], + "scopes": [] + }, "contextRewrites": [ { "kind": "KeyRenamer", "path": [ "..", - "... on A", + "... on B", "prop" ], "renameKeyTo": "contextualArgument_1_1" } ], - "schemaAwareHash": "c50ca82d402a330c1b35a6d76332094c40b00d6dec6f6b2a9b0a32ced68f4e95", - "authorization": { - "is_authenticated": false, - "scopes": [], - "policies": [] - } - } - }, - { - "kind": "Flatten", - "path": [ - "", - "k|[B]", - "v" - ], - "node": { + "id": null, + "inputRewrites": null, "kind": "Fetch", - "serviceName": "Subgraph1", + "operation": "query QueryUnion__Subgraph1__2($representations:[_Any!]!$contextualArgument_1_1:String){_entities(representations:$representations){...on V{field(a:$contextualArgument_1_1)}}}", + "operationKind": "query", + "operationName": "QueryUnion__Subgraph1__2", + "outputRewrites": null, "requires": [ { "kind": "InlineFragment", - "typeCondition": "V", "selections": [ { "kind": "Field", @@ -115,36 +130,21 @@ expression: response "kind": "Field", "name": "id" } - ] - } - ], - "variableUsages": [ - "contextualArgument_1_1" - ], - "operation": "query QueryUnion__Subgraph1__2($representations:[_Any!]!$contextualArgument_1_1:String){_entities(representations:$representations){...on V{field(a:$contextualArgument_1_1)}}}", - "operationName": "QueryUnion__Subgraph1__2", - "operationKind": "query", - "id": null, - "inputRewrites": null, - "outputRewrites": null, - "contextRewrites": [ - { - "kind": "KeyRenamer", - "path": [ - "..", - "... on B", - "prop" ], - "renameKeyTo": "contextualArgument_1_1" + "typeCondition": "V" } ], "schemaAwareHash": "ec99886497fee9b4f13565e19cadb13ae85c83de93acb53f298944b7a29e630e", - "authorization": { - "is_authenticated": false, - "scopes": [], - "policies": [] - } - } + "serviceName": "Subgraph1", + "variableUsages": [ + "contextualArgument_1_1" + ] + }, + "path": [ + "", + "k|[B]", + "v" + ] } ] } From 403841a1011de8c61576c534f9b3af88789d320b Mon Sep 17 00:00:00 2001 From: o0Ignition0o Date: Fri, 17 May 2024 15:11:58 +0200 Subject: [PATCH 43/69] lint --- apollo-router/src/query_planner/fetch.rs | 3 +-- apollo-router/src/query_planner/subgraph_context.rs | 6 +++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/apollo-router/src/query_planner/fetch.rs b/apollo-router/src/query_planner/fetch.rs index 87c46b8bdb..1a1f109cc4 100644 --- a/apollo-router/src/query_planner/fetch.rs +++ b/apollo-router/src/query_planner/fetch.rs @@ -33,12 +33,11 @@ use crate::json_ext::Value; use crate::json_ext::ValueExt; use crate::plugins::authorization::AuthorizationPlugin; use crate::plugins::authorization::CacheKeyMetadata; +use crate::query_planner::subgraph_context::SubgraphContext; use crate::services::SubgraphRequest; use crate::spec::query::change::QueryHashVisitor; use crate::spec::Schema; -use crate::query_planner::subgraph_context::SubgraphContext; - /// GraphQL operation type. #[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] diff --git a/apollo-router/src/query_planner/subgraph_context.rs b/apollo-router/src/query_planner/subgraph_context.rs index 28849c4c3c..4dc7092883 100644 --- a/apollo-router/src/query_planner/subgraph_context.rs +++ b/apollo-router/src/query_planner/subgraph_context.rs @@ -1,3 +1,6 @@ +use std::collections::HashMap; +use std::collections::HashSet; + use apollo_compiler::ast; use apollo_compiler::ast::Name; use apollo_compiler::ast::VariableDefinition; @@ -11,13 +14,10 @@ use apollo_compiler::ExecutableDocument; use apollo_compiler::Node; use serde_json_bytes::ByteString; use serde_json_bytes::Map; -use std::collections::HashMap; -use std::collections::HashSet; use super::fetch::SubgraphOperation; use super::rewrites::DataKeyRenamer; use super::rewrites::DataRewrite; - use crate::json_ext::Path; use crate::json_ext::PathElement; use crate::json_ext::Value; From 87c328031d39d18c8b764662d84660fe32594961 Mon Sep 17 00:00:00 2001 From: o0Ignition0o Date: Fri, 17 May 2024 15:19:51 +0200 Subject: [PATCH 44/69] clippy --- apollo-router/src/query_planner/subgraph_context.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/apollo-router/src/query_planner/subgraph_context.rs b/apollo-router/src/query_planner/subgraph_context.rs index 4dc7092883..333f94f284 100644 --- a/apollo-router/src/query_planner/subgraph_context.rs +++ b/apollo-router/src/query_planner/subgraph_context.rs @@ -87,7 +87,7 @@ impl<'a> SubgraphContext<'a> { context_rewrites: &'a Option>, ) -> Option> { if let Some(rewrites) = context_rewrites { - if rewrites.len() > 0 { + if !rewrites.is_empty() { return Some(SubgraphContext { data, schema, @@ -236,18 +236,18 @@ pub(crate) fn build_operation_with_aliasing( // it is a field selection for _entities, so it's ok to reach in and give it an alias let mut selection_set = op.selection_set.clone(); transform_selection_set(&mut selection_set, arguments, i, true); - selection_set.selections.get_mut(0).map(|selection| { + if let Some(selection) = selection_set.selections.get_mut(0) { if let Selection::Field(f) = selection { let field = f.make_mut(); field.alias = Some(Name::new_unchecked(format!("_{}", i).into())); } selections.push(selection.clone()); - }); + }; } let mut ed = ExecutableDocument::new(); ed.insert_operation(Operation { - operation_type: op.operation_type.clone(), + operation_type: op.operation_type, name: op.name.clone(), directives: op.directives.clone(), variables: new_variables, @@ -301,7 +301,7 @@ fn transform_selection_set( // transforms the variable name on the field argment fn transform_field_arguments( - arguments_in_selection: &mut Vec>, + arguments_in_selection: &mut [Node], arguments: &HashSet, index: usize, ) { From 44d64e32faa258980755a6e27c3a99c43ed7f224 Mon Sep 17 00:00:00 2001 From: o0Ignition0o Date: Fri, 17 May 2024 16:00:06 +0200 Subject: [PATCH 45/69] duplicate fixture to address non ordering of variables --- .../tests/fixtures/set_context/one.json | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/apollo-router/tests/fixtures/set_context/one.json b/apollo-router/tests/fixtures/set_context/one.json index 0e2dfecfec..db63f8ec4f 100644 --- a/apollo-router/tests/fixtures/set_context/one.json +++ b/apollo-router/tests/fixtures/set_context/one.json @@ -221,6 +221,34 @@ } } }, + { + "request": { + "query": "query QueryLL__Subgraph1__1($representations: [_Any!]!, $contextualArgument_1_0_0: String, $contextualArgument_1_0_1: String) { _0: _entities(representations: $representations) { ... on U { field(a: $contextualArgument_1_0_0) } } _1: _entities(representations: $representations) { ... on U { field(a: $contextualArgument_1_0_1) } } }", + "operationName": "QueryLL__Subgraph1__1", + "variables": { + "contextualArgument_1_0_1": "prop value 2", + "contextualArgument_1_0_0": "prop value 1", + "representations": [ + { "__typename": "U", "id": "3" }, + { "__typename": "U", "id": "4" } + ] + } + }, + "response": { + "data": { + "_entities": [ + { + "id": "3", + "field": 3456 + }, + { + "id": "4", + "field": 4567 + } + ] + } + } + }, { "request": { "query": "query QueryUnion__Subgraph1__1($representations:[_Any!]!$contextualArgument_1_1:String){_entities(representations:$representations){...on V{field(a:$contextualArgument_1_1)}}}", From 3da3d82216cd9871dc15fb12fd6b3470c14e1c5b Mon Sep 17 00:00:00 2001 From: Chris Lenfest Date: Fri, 17 May 2024 09:11:33 -0500 Subject: [PATCH 46/69] cargo lock update --- Cargo.lock | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 51c0065f6c..9c4adecea3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3819,16 +3819,6 @@ dependencies = [ "threadpool", ] -[[package]] -name = "libz-ng-sys" -version = "1.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dd9f43e75536a46ee0f92b758f6b63846e594e86638c61a9251338a65baea63" -dependencies = [ - "cmake", - "libc", -] - [[package]] name = "libz-sys" version = "1.1.12" From c6acd13d545e948a85370eaef19cecc997ad9591 Mon Sep 17 00:00:00 2001 From: Chris Lenfest Date: Fri, 17 May 2024 09:26:36 -0500 Subject: [PATCH 47/69] Add changeset --- .changesets/feat_clenfest_set_context.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changesets/feat_clenfest_set_context.md diff --git a/.changesets/feat_clenfest_set_context.md b/.changesets/feat_clenfest_set_context.md new file mode 100644 index 0000000000..97d06149c0 --- /dev/null +++ b/.changesets/feat_clenfest_set_context.md @@ -0,0 +1,5 @@ +### Add ability for router to deal with query plans with contextual rewrites ([PR #5097](https://github.com/apollographql/router/pull/5097)) + +Adds the ability for the router to execute query plans with context rewrites on them. These are generated by the @fromContext directive and are used to map a Value in the collected data JSON onto a variable which will in turn be used as an argument to a field resolver. + +By [@clenfest](https://github.com/clenfest) in https://github.com/apollographql/router/pull/5097 \ No newline at end of file From faaf790f6be0479afda8a88c27075dff8d45523c Mon Sep 17 00:00:00 2001 From: Chris Lenfest Date: Fri, 17 May 2024 10:04:02 -0500 Subject: [PATCH 48/69] updating redis cache keys --- apollo-router/tests/integration/redis.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/apollo-router/tests/integration/redis.rs b/apollo-router/tests/integration/redis.rs index 4b3aa46d78..dd5a27c2fd 100644 --- a/apollo-router/tests/integration/redis.rs +++ b/apollo-router/tests/integration/redis.rs @@ -28,7 +28,7 @@ async fn query_planner() -> Result<(), BoxError> { // 2. run `docker compose up -d` and connect to the redis container by running `docker-compose exec redis /bin/bash`. // 3. Run the `redis-cli` command from the shell and start the redis `monitor` command. // 4. Run this test and yank the updated cache key from the redis logs. - let known_cache_key = "plan:0:v2.7.5:16385ebef77959fcdc520ad507eb1f7f7df28f1d54a0569e3adabcb4cd00d7ce:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:9c26cb1f820a78848ba3d5d3295c16aa971368c5295422fd33cc19d4a6006a9c"; + let known_cache_key = "plan:0:v2.8.0-alpha.0:a9e605fa09adc5a4b824e690b4de6f160d47d84ede5956b58a7d300cca1f7204:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:1910d63916aae7a1066cb8c7d622fc3a8e363ed1b6ac8e214deed4046abae85c"; let config = RedisConfig::from_url("redis://127.0.0.1:6379").unwrap(); let client = RedisClient::new(config, None, None, None); @@ -902,7 +902,7 @@ async fn connection_failure_blocks_startup() { async fn query_planner_redis_update_query_fragments() { test_redis_query_plan_config_update( include_str!("fixtures/query_planner_redis_config_update_query_fragments.router.yaml"), - "plan:0:v2.7.5:a9e605fa09adc5a4b824e690b4de6f160d47d84ede5956b58a7d300cca1f7204:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:ae8b525534cb7446a34715fc80edd41d4d29aa65c5f39f9237d4ed8459e3fe82", + "plan:0:v2.8.0-alpha.0:a9e605fa09adc5a4b824e690b4de6f160d47d84ede5956b58a7d300cca1f7204:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:ae8b525534cb7446a34715fc80edd41d4d29aa65c5f39f9237d4ed8459e3fe82", ) .await; } @@ -921,7 +921,7 @@ async fn query_planner_redis_update_planner_mode() { async fn query_planner_redis_update_introspection() { test_redis_query_plan_config_update( include_str!("fixtures/query_planner_redis_config_update_introspection.router.yaml"), - "plan:0:v2.7.5:a9e605fa09adc5a4b824e690b4de6f160d47d84ede5956b58a7d300cca1f7204:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:1910d63916aae7a1066cb8c7d622fc3a8e363ed1b6ac8e214deed4046abae85c", + "plan:0:v2.8.0-alpha.0:a9e605fa09adc5a4b824e690b4de6f160d47d84ede5956b58a7d300cca1f7204:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:1910d63916aae7a1066cb8c7d622fc3a8e363ed1b6ac8e214deed4046abae85c", ) .await; } @@ -930,7 +930,7 @@ async fn query_planner_redis_update_introspection() { async fn query_planner_redis_update_defer() { test_redis_query_plan_config_update( include_str!("fixtures/query_planner_redis_config_update_defer.router.yaml"), - "plan:0:v2.7.5:a9e605fa09adc5a4b824e690b4de6f160d47d84ede5956b58a7d300cca1f7204:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:8a17c5b196af5e3a18d24596424e9849d198f456dd48297b852a5f2ca847169b", + "plan:0:v2.8.0-alpha.0:a9e605fa09adc5a4b824e690b4de6f160d47d84ede5956b58a7d300cca1f7204:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:8a17c5b196af5e3a18d24596424e9849d198f456dd48297b852a5f2ca847169b", ) .await; } @@ -941,7 +941,7 @@ async fn query_planner_redis_update_type_conditional_fetching() { include_str!( "fixtures/query_planner_redis_config_update_type_conditional_fetching.router.yaml" ), - "plan:0:v2.7.5:a9e605fa09adc5a4b824e690b4de6f160d47d84ede5956b58a7d300cca1f7204:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:275f78612ed3d45cdf6bf328ef83e368b5a44393bd8c944d4a7d694aed61f017", + "plan:0:v2.8.0-alpha.0:a9e605fa09adc5a4b824e690b4de6f160d47d84ede5956b58a7d300cca1f7204:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:275f78612ed3d45cdf6bf328ef83e368b5a44393bd8c944d4a7d694aed61f017", ) .await; } @@ -952,7 +952,7 @@ async fn query_planner_redis_update_reuse_query_fragments() { include_str!( "fixtures/query_planner_redis_config_update_reuse_query_fragments.router.yaml" ), - "plan:0:v2.7.5:a9e605fa09adc5a4b824e690b4de6f160d47d84ede5956b58a7d300cca1f7204:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:15fbb62c94e8da6ea78f28a6eb86a615dcaf27ff6fd0748fac4eb614b0b17662", + "plan:0:v2.8.0-alpha.0:a9e605fa09adc5a4b824e690b4de6f160d47d84ede5956b58a7d300cca1f7204:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:15fbb62c94e8da6ea78f28a6eb86a615dcaf27ff6fd0748fac4eb614b0b17662", ) .await; } @@ -972,7 +972,7 @@ async fn test_redis_query_plan_config_update(updated_config: &str, new_cache_key router.assert_started().await; router.clear_redis_cache().await; - let starting_key = "plan:0:v2.7.5:a9e605fa09adc5a4b824e690b4de6f160d47d84ede5956b58a7d300cca1f7204:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:1910d63916aae7a1066cb8c7d622fc3a8e363ed1b6ac8e214deed4046abae85c"; + let starting_key = "plan:0:v2.8.0-alpha.0:a9e605fa09adc5a4b824e690b4de6f160d47d84ede5956b58a7d300cca1f7204:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:1910d63916aae7a1066cb8c7d622fc3a8e363ed1b6ac8e214deed4046abae85c"; router.execute_default_query().await; router.assert_redis_cache_contains(starting_key, None).await; router.update_config(updated_config).await; From 2212b9d8045aa72b12404dc0aae5fe1b4d7a9466 Mon Sep 17 00:00:00 2001 From: Chris Lenfest Date: Fri, 17 May 2024 10:54:45 -0500 Subject: [PATCH 49/69] redis key update --- apollo-router/tests/integration/redis.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apollo-router/tests/integration/redis.rs b/apollo-router/tests/integration/redis.rs index dd5a27c2fd..60519bc8c1 100644 --- a/apollo-router/tests/integration/redis.rs +++ b/apollo-router/tests/integration/redis.rs @@ -28,7 +28,7 @@ async fn query_planner() -> Result<(), BoxError> { // 2. run `docker compose up -d` and connect to the redis container by running `docker-compose exec redis /bin/bash`. // 3. Run the `redis-cli` command from the shell and start the redis `monitor` command. // 4. Run this test and yank the updated cache key from the redis logs. - let known_cache_key = "plan:0:v2.8.0-alpha.0:a9e605fa09adc5a4b824e690b4de6f160d47d84ede5956b58a7d300cca1f7204:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:1910d63916aae7a1066cb8c7d622fc3a8e363ed1b6ac8e214deed4046abae85c"; + let known_cache_key = "plan:0:v2.8.0-alpha.0:16385ebef77959fcdc520ad507eb1f7f7df28f1d54a0569e3adabcb4cd00d7ce:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:9c26cb1f820a78848ba3d5d3295c16aa971368c5295422fd33cc19d4a6006a9c"; let config = RedisConfig::from_url("redis://127.0.0.1:6379").unwrap(); let client = RedisClient::new(config, None, None, None); From 323b263cb6c9e205a018d827b4713693921c4149 Mon Sep 17 00:00:00 2001 From: Chris Lenfest Date: Fri, 17 May 2024 10:57:54 -0500 Subject: [PATCH 50/69] Update apollo-router/src/query_planner/fetch.rs Co-authored-by: Gary Pennington --- apollo-router/src/query_planner/fetch.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apollo-router/src/query_planner/fetch.rs b/apollo-router/src/query_planner/fetch.rs index 1a1f109cc4..a1a93deeb2 100644 --- a/apollo-router/src/query_planner/fetch.rs +++ b/apollo-router/src/query_planner/fetch.rs @@ -33,7 +33,7 @@ use crate::json_ext::Value; use crate::json_ext::ValueExt; use crate::plugins::authorization::AuthorizationPlugin; use crate::plugins::authorization::CacheKeyMetadata; -use crate::query_planner::subgraph_context::SubgraphContext; +use super::subgraph_context::SubgraphContext; use crate::services::SubgraphRequest; use crate::spec::query::change::QueryHashVisitor; use crate::spec::Schema; From 089270f8d7ad2f42bf7540817b76e1872cc8621d Mon Sep 17 00:00:00 2001 From: Chris Lenfest Date: Fri, 17 May 2024 10:58:03 -0500 Subject: [PATCH 51/69] Update apollo-router/src/query_planner/mod.rs Co-authored-by: Gary Pennington --- apollo-router/src/query_planner/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apollo-router/src/query_planner/mod.rs b/apollo-router/src/query_planner/mod.rs index 3b3ea97f67..58285e3638 100644 --- a/apollo-router/src/query_planner/mod.rs +++ b/apollo-router/src/query_planner/mod.rs @@ -20,7 +20,7 @@ mod labeler; mod plan; pub(crate) mod rewrites; mod selection; -pub(crate) mod subgraph_context; +mod subgraph_context; pub(crate) mod subscription; pub(crate) const FETCH_SPAN_NAME: &str = "fetch"; From ec6cd98adffb75cb540adbfba6f9f8427a11a826 Mon Sep 17 00:00:00 2001 From: Chris Lenfest Date: Sun, 19 May 2024 21:49:33 -0500 Subject: [PATCH 52/69] add some failure tests --- apollo-router/src/query_planner/fetch.rs | 1 - .../tests/fixtures/set_context/one.json | 63 +++++++ .../tests/fixtures/set_context/two.json | 23 +++ apollo-router/tests/set_context.rs | 55 +++++- ...__set_context_dependent_fetch_failure.snap | 98 ++++++++++ ...__set_context_unrelated_fetch_failure.snap | 170 ++++++++++++++++++ 6 files changed, 408 insertions(+), 2 deletions(-) create mode 100644 apollo-router/tests/snapshots/set_context__set_context_dependent_fetch_failure.snap create mode 100644 apollo-router/tests/snapshots/set_context__set_context_unrelated_fetch_failure.snap diff --git a/apollo-router/src/query_planner/fetch.rs b/apollo-router/src/query_planner/fetch.rs index a1a93deeb2..b99ffa8fc9 100644 --- a/apollo-router/src/query_planner/fetch.rs +++ b/apollo-router/src/query_planner/fetch.rs @@ -395,7 +395,6 @@ impl FetchNode { let alias_query_string; // this exists outside the if block to allow the as_str() to be longer lived let aliased_operation = if let Some(ctx_arg) = contextual_arguments { - // TODO: subgraph_schemas should be a HashMap of NodeStr or Name. if let Some(subgraph_schema) = parameters.subgraph_schemas.get(&service_name.to_string()) { diff --git a/apollo-router/tests/fixtures/set_context/one.json b/apollo-router/tests/fixtures/set_context/one.json index db63f8ec4f..e552a0f47b 100644 --- a/apollo-router/tests/fixtures/set_context/one.json +++ b/apollo-router/tests/fixtures/set_context/one.json @@ -346,6 +346,69 @@ ] } } + }, + { + "request": { + "query": "query Query_fetch_failure__Subgraph1__0{t{__typename prop id u{__typename id}}}", + "operationName": "Query_fetch_failure__Subgraph1__0" + }, + "response": { + "data": { + "t": { + "__typename": "T", + "prop": "prop value", + "id": "1", + "u": { + "__typename": "U", + "id": "1" + } + } + } + } + }, + { + "request": { + "query": "query Query_fetch_failure__Subgraph1__2($representations:[_Any!]!$contextualArgument_1_0:String){_entities(representations:$representations){...on U{field(a:$contextualArgument_1_0)}}}", + "operationName": "Query_fetch_failure__Subgraph1__2", + "variables": { + "contextualArgument_1_0": "prop value", + "representations": [{ "__typename": "U", "id": "1" }] + } + }, + "response": { + "data": { + "t": { + "__typename": "T", + "prop": "prop value", + "id": "1", + "u": { + "__typename": "U", + "id": "1" + } + } + } + } + }, + { + "request": { + "query": "query Query_fetch_dependent_failure__Subgraph1__0{t{__typename prop id u{__typename id}}}", + "operationName": "Query_fetch_dependent_failure__Subgraph1__0" + }, + "response": { + "response": { + "data": null, + "errors": [{ + "message": "Some error", + "locations": [ + { + "line": 3, + "column": 5 + } + ], + "path": ["t", "u"] + }] + } + } } ] } diff --git a/apollo-router/tests/fixtures/set_context/two.json b/apollo-router/tests/fixtures/set_context/two.json index 986a310a9b..6ab18d052b 100644 --- a/apollo-router/tests/fixtures/set_context/two.json +++ b/apollo-router/tests/fixtures/set_context/two.json @@ -15,6 +15,29 @@ ] } } + }, + { + "request": { + "query": "query Query_fetch_failure__Subgraph2__1($representations:[_Any!]!){_entities(representations:$representations){...on U{b}}}", + "operationName": "Query_fetch_failure__Subgraph2__1", + "variables": { + "representations": [{ "__typename": "U", "id": "1" }] + } + }, + "response": { + "data": null, + "errors": [{ + "message": "Some error", + "locations": [ + { + "line": 3, + "column": 5 + } + ], + "path": ["t", "u"] + } + ] + } } ] } diff --git a/apollo-router/tests/set_context.rs b/apollo-router/tests/set_context.rs index ccb790c678..7684bd85b1 100644 --- a/apollo-router/tests/set_context.rs +++ b/apollo-router/tests/set_context.rs @@ -220,7 +220,60 @@ async fn test_set_context_with_null() { #[tokio::test(flavor = "multi_thread")] async fn test_set_context_type_mismatch() { static QUERY: &str = r#" - query Query_type_mismatch { + query Query_fetch_failure { + t { + id + u { + field + } + } + }"#; + + let response = run_single_request( + QUERY, + &[ + ("Subgraph1", include_str!("fixtures/set_context/one.json")), + ("Subgraph2", include_str!("fixtures/set_context/two.json")), + ], + ) + .await; + + snap!(response); +} + +// fetch from unrelated (to context) subgraph fails +// validates that the error propagation is correct +#[tokio::test(flavor = "multi_thread")] +async fn test_set_context_unrelated_fetch_failure() { + static QUERY: &str = r#" + query Query_fetch_failure { + t { + id + u { + field + b + } + } + }"#; + + let response = run_single_request( + QUERY, + &[ + ("Subgraph1", include_str!("fixtures/set_context/one.json")), + ("Subgraph2", include_str!("fixtures/set_context/two.json")), + ], + ) + .await; + + snap!(response); +} + +// subgraph fetch fails where context depends on results of fetch. +// validates that no fetch will get called that passes context +#[tokio::test(flavor = "multi_thread")] +async fn test_set_context_dependent_fetch_failure() { + static QUERY: &str = r#" + query Query_fetch_dependent_failure { t { id u { diff --git a/apollo-router/tests/snapshots/set_context__set_context_dependent_fetch_failure.snap b/apollo-router/tests/snapshots/set_context__set_context_dependent_fetch_failure.snap new file mode 100644 index 0000000000..703d8f9c59 --- /dev/null +++ b/apollo-router/tests/snapshots/set_context__set_context_dependent_fetch_failure.snap @@ -0,0 +1,98 @@ +--- +source: apollo-router/tests/set_context.rs +expression: response +--- +{ + "data": null, + "extensions": { + "apolloQueryPlan": { + "object": { + "kind": "QueryPlan", + "node": { + "kind": "Sequence", + "nodes": [ + { + "authorization": { + "is_authenticated": false, + "policies": [], + "scopes": [] + }, + "contextRewrites": null, + "id": null, + "inputRewrites": null, + "kind": "Fetch", + "operation": "query Query_fetch_dependent_failure__Subgraph1__0{t{__typename prop id u{__typename id}}}", + "operationKind": "query", + "operationName": "Query_fetch_dependent_failure__Subgraph1__0", + "outputRewrites": null, + "schemaAwareHash": "595c36c322602fefc4658fc0070973b51800c2d2debafae5571a7c9811d80745", + "serviceName": "Subgraph1", + "variableUsages": [] + }, + { + "kind": "Flatten", + "node": { + "authorization": { + "is_authenticated": false, + "policies": [], + "scopes": [] + }, + "contextRewrites": [ + { + "kind": "KeyRenamer", + "path": [ + "..", + "... on T", + "prop" + ], + "renameKeyTo": "contextualArgument_1_0" + } + ], + "id": null, + "inputRewrites": null, + "kind": "Fetch", + "operation": "query Query_fetch_dependent_failure__Subgraph1__1($representations:[_Any!]!$contextualArgument_1_0:String){_entities(representations:$representations){...on U{field(a:$contextualArgument_1_0)}}}", + "operationKind": "query", + "operationName": "Query_fetch_dependent_failure__Subgraph1__1", + "outputRewrites": null, + "requires": [ + { + "kind": "InlineFragment", + "selections": [ + { + "kind": "Field", + "name": "__typename" + }, + { + "kind": "Field", + "name": "id" + } + ], + "typeCondition": "U" + } + ], + "schemaAwareHash": "37bef7ad43bb477cdec4dfc02446bd2e11a6919dc14ab90e266af85fefde4abd", + "serviceName": "Subgraph1", + "variableUsages": [ + "contextualArgument_1_0" + ] + }, + "path": [ + "", + "t", + "u" + ] + } + ] + } + }, + "text": "QueryPlan {\n Sequence {\n Fetch(service: \"Subgraph1\") {\n {\n t {\n __typename\n prop\n id\n u {\n __typename\n id\n }\n }\n }\n },\n Flatten(path: \".t.u\") {\n Fetch(service: \"Subgraph1\") {\n {\n ... on U {\n __typename\n id\n }\n } =>\n {\n ... on U {\n field(a: $contextualArgument_1_0)\n }\n }\n },\n },\n },\n}" + }, + "valueCompletion": [ + { + "message": "Cannot return null for non-nullable field Query.t", + "path": [] + } + ] + } +} diff --git a/apollo-router/tests/snapshots/set_context__set_context_unrelated_fetch_failure.snap b/apollo-router/tests/snapshots/set_context__set_context_unrelated_fetch_failure.snap new file mode 100644 index 0000000000..605fd4570a --- /dev/null +++ b/apollo-router/tests/snapshots/set_context__set_context_unrelated_fetch_failure.snap @@ -0,0 +1,170 @@ +--- +source: apollo-router/tests/set_context.rs +expression: response +--- +{ + "data": null, + "errors": [ + { + "message": "Some error", + "path": [ + "t", + "u" + ] + } + ], + "extensions": { + "apolloQueryPlan": { + "object": { + "kind": "QueryPlan", + "node": { + "kind": "Sequence", + "nodes": [ + { + "authorization": { + "is_authenticated": false, + "policies": [], + "scopes": [] + }, + "contextRewrites": null, + "id": null, + "inputRewrites": null, + "kind": "Fetch", + "operation": "query Query_fetch_failure__Subgraph1__0{t{__typename prop id u{__typename id}}}", + "operationKind": "query", + "operationName": "Query_fetch_failure__Subgraph1__0", + "outputRewrites": null, + "schemaAwareHash": "1813ba1c272be0201096b4c4c963a07638e4f4b4ac1b97e0d90d634f2fcbac11", + "serviceName": "Subgraph1", + "variableUsages": [] + }, + { + "kind": "Parallel", + "nodes": [ + { + "kind": "Flatten", + "node": { + "authorization": { + "is_authenticated": false, + "policies": [], + "scopes": [] + }, + "contextRewrites": null, + "id": null, + "inputRewrites": null, + "kind": "Fetch", + "operation": "query Query_fetch_failure__Subgraph2__1($representations:[_Any!]!){_entities(representations:$representations){...on U{b}}}", + "operationKind": "query", + "operationName": "Query_fetch_failure__Subgraph2__1", + "outputRewrites": null, + "requires": [ + { + "kind": "InlineFragment", + "selections": [ + { + "kind": "Field", + "name": "__typename" + }, + { + "kind": "Field", + "name": "id" + } + ], + "typeCondition": "U" + } + ], + "schemaAwareHash": "1fdff97ad7facf07690c3e75e3dc7f1b11ff509268ef999250912a728e7a94c9", + "serviceName": "Subgraph2", + "variableUsages": [] + }, + "path": [ + "", + "t", + "u" + ] + }, + { + "kind": "Flatten", + "node": { + "authorization": { + "is_authenticated": false, + "policies": [], + "scopes": [] + }, + "contextRewrites": [ + { + "kind": "KeyRenamer", + "path": [ + "..", + "... on T", + "prop" + ], + "renameKeyTo": "contextualArgument_1_0" + } + ], + "id": null, + "inputRewrites": null, + "kind": "Fetch", + "operation": "query Query_fetch_failure__Subgraph1__2($representations:[_Any!]!$contextualArgument_1_0:String){_entities(representations:$representations){...on U{field(a:$contextualArgument_1_0)}}}", + "operationKind": "query", + "operationName": "Query_fetch_failure__Subgraph1__2", + "outputRewrites": null, + "requires": [ + { + "kind": "InlineFragment", + "selections": [ + { + "kind": "Field", + "name": "__typename" + }, + { + "kind": "Field", + "name": "id" + } + ], + "typeCondition": "U" + } + ], + "schemaAwareHash": "c9c571eac5df81ff34e5e228934d029ed322640c97ab6ad061cbee3cd81040dc", + "serviceName": "Subgraph1", + "variableUsages": [ + "contextualArgument_1_0" + ] + }, + "path": [ + "", + "t", + "u" + ] + } + ] + } + ] + } + }, + "text": "QueryPlan {\n Sequence {\n Fetch(service: \"Subgraph1\") {\n {\n t {\n __typename\n prop\n id\n u {\n __typename\n id\n }\n }\n }\n },\n Parallel {\n Flatten(path: \".t.u\") {\n Fetch(service: \"Subgraph2\") {\n {\n ... on U {\n __typename\n id\n }\n } =>\n {\n ... on U {\n b\n }\n }\n },\n },\n Flatten(path: \".t.u\") {\n Fetch(service: \"Subgraph1\") {\n {\n ... on U {\n __typename\n id\n }\n } =>\n {\n ... on U {\n field(a: $contextualArgument_1_0)\n }\n }\n },\n },\n },\n },\n}" + }, + "valueCompletion": [ + { + "message": "Cannot return null for non-nullable field U.field", + "path": [ + "t", + "u" + ] + }, + { + "message": "Cannot return null for non-nullable field T.u", + "path": [ + "t", + "u" + ] + }, + { + "message": "Cannot return null for non-nullable field T!.t", + "path": [ + "t" + ] + } + ] + } +} From 17302931b1930316b0d7331f71a68d2444080495 Mon Sep 17 00:00:00 2001 From: Chris Lenfest Date: Mon, 20 May 2024 00:46:27 -0500 Subject: [PATCH 53/69] split out transform_operation as a separate function --- apollo-router/src/query_planner/fetch.rs | 2 +- .../src/query_planner/subgraph_context.rs | 156 +++++++++++------- 2 files changed, 94 insertions(+), 64 deletions(-) diff --git a/apollo-router/src/query_planner/fetch.rs b/apollo-router/src/query_planner/fetch.rs index b99ffa8fc9..a20ad3ffda 100644 --- a/apollo-router/src/query_planner/fetch.rs +++ b/apollo-router/src/query_planner/fetch.rs @@ -20,6 +20,7 @@ use super::selection::execute_selection_set; use super::selection::Selection; use super::subgraph_context::build_operation_with_aliasing; use super::subgraph_context::ContextualArguments; +use super::subgraph_context::SubgraphContext; use crate::error::Error; use crate::error::FetchError; use crate::error::ValidationErrors; @@ -33,7 +34,6 @@ use crate::json_ext::Value; use crate::json_ext::ValueExt; use crate::plugins::authorization::AuthorizationPlugin; use crate::plugins::authorization::CacheKeyMetadata; -use super::subgraph_context::SubgraphContext; use crate::services::SubgraphRequest; use crate::spec::query::change::QueryHashVisitor; use crate::spec::Schema; diff --git a/apollo-router/src/query_planner/subgraph_context.rs b/apollo-router/src/query_planner/subgraph_context.rs index 333f94f284..17210d6fd0 100644 --- a/apollo-router/src/query_planner/subgraph_context.rs +++ b/apollo-router/src/query_planner/subgraph_context.rs @@ -208,66 +208,91 @@ pub(crate) fn build_operation_with_aliasing( contextual_arguments: &ContextualArguments, subgraph_schema: &Valid, ) -> Result, ContextBatchingError> { - let mut selections: Vec = vec![]; let ContextualArguments { arguments, count } = contextual_arguments; let parsed_document = subgraph_operation.as_parsed(subgraph_schema); - if let Ok(document) = parsed_document { - // TODO: Can there be more than one named operation? - // Can there be an anonymous operation? - if let Some((_, op)) = document.named_operations.first() { - let mut new_variables: Vec> = vec![]; - op.variables.iter().for_each(|v| { - if arguments.contains(v.name.as_str()) { - for i in 0..*count { - new_variables.push(Node::new(VariableDefinition { - name: Name::new_unchecked(format!("{}_{}", v.name.as_str(), i).into()), - ty: v.ty.clone(), - default_value: v.default_value.clone(), - directives: v.directives.clone(), - })); - } - } else { - new_variables.push(v.clone()); - } - }); - for i in 0..*count { - // If we are aliasing, we know that there is only one selection in the top level SelectionSet - // it is a field selection for _entities, so it's ok to reach in and give it an alias - let mut selection_set = op.selection_set.clone(); - transform_selection_set(&mut selection_set, arguments, i, true); - if let Some(selection) = selection_set.selections.get_mut(0) { - if let Selection::Field(f) = selection { - let field = f.make_mut(); - field.alias = Some(Name::new_unchecked(format!("_{}", i).into())); - } - selections.push(selection.clone()); - }; - } + let mut ed = ExecutableDocument::new(); + + // for every operation in the document, go ahead and transform even though it's likely that only one exists + if let Ok(document) = parsed_document { + if let Some(anonymous_op) = &document.anonymous_operation { + let mut cloned = anonymous_op.clone(); + transform_operation(&mut cloned, arguments, count)?; + ed.insert_operation(cloned); + } - let mut ed = ExecutableDocument::new(); - ed.insert_operation(Operation { - operation_type: op.operation_type, - name: op.name.clone(), - directives: op.directives.clone(), - variables: new_variables, - selection_set: SelectionSet { - ty: op.selection_set.ty.clone(), - selections, - }, - }); - - return ed - .validate(subgraph_schema) - .map_err(ContextBatchingError::InvalidDocumentGenerated); + for (_, op) in &document.named_operations { + let mut cloned = op.clone(); + transform_operation(&mut cloned, arguments, count)?; + ed.insert_operation(cloned); } + + return ed + .validate(subgraph_schema) + .map_err(ContextBatchingError::InvalidDocumentGenerated); } Err(ContextBatchingError::NoSelectionSet) } -// adds an alias that aligns with the index for this selection -fn add_alias_to_selection(selection: &mut executable::Field, index: usize) { - selection.alias = Some(Name::new_unchecked(format!("_{}", index).into())); +fn transform_operation(operation: &mut Node, arguments: &HashSet, count: &usize) -> Result<(), ContextBatchingError> { + let mut selections: Vec = vec![]; + let mut new_variables: Vec> = vec![]; + operation.variables.iter().for_each(|v| { + if arguments.contains(v.name.as_str()) { + for i in 0..*count { + new_variables.push(Node::new(VariableDefinition { + name: Name::new_unchecked(format!("{}_{}", v.name.as_str(), i).into()), + ty: v.ty.clone(), + default_value: v.default_value.clone(), + directives: v.directives.clone(), + })); + } + } else { + new_variables.push(v.clone()); + } + }); + + // there should only be one selection that is a field selection that we're going to rename, but let's count to be sure + // and error if that's not the case + // also it's possible that there could be an inline fragment, so if that's the case, just add those to the new selections once + let mut field_selection: Option> = None; + for selection in &operation.selection_set.selections { + match selection { + Selection::Field(f) => { + if field_selection != None { + // if we get here, there is more than one field selection, which should not be the case + // at the top level of a _entities selection set + return Err(ContextBatchingError::UnexpectedSelection); + } + field_selection = Some(f.clone()); + }, + _ => { + // again, if we get here, something is wrong. _entities selection sets should have just one field selection + return Err(ContextBatchingError::UnexpectedSelection); + } + } + }; + + let field_selection = field_selection.ok_or(ContextBatchingError::UnexpectedSelection)?; + + for i in 0..*count { + // If we are aliasing, we know that there is only one selection in the top level SelectionSet + // it is a field selection for _entities, so it's ok to reach in and give it an alias + let mut cloned = field_selection.clone(); + let cfs = cloned.make_mut(); + cfs.alias = Some(Name::new_unchecked(format!("_{}", i).into())); + + transform_field_arguments(&mut cfs.arguments, arguments, i); + transform_selection_set(&mut cfs.selection_set, arguments, i); + selections.push(Selection::Field(cloned)); + } + let operation = operation.make_mut(); + operation.variables = new_variables; + operation.selection_set = SelectionSet { + ty: operation.selection_set.ty.clone(), + selections, + }; + Ok(()) } // This function will take the selection set (which has been cloned from the original) @@ -277,7 +302,6 @@ fn transform_selection_set( selection_set: &mut SelectionSet, arguments: &HashSet, index: usize, - add_alias: bool, // at the top level, we'll add an alias to field selections ) { selection_set .selections @@ -286,14 +310,11 @@ fn transform_selection_set( executable::Selection::Field(node) => { let node = node.make_mut(); transform_field_arguments(&mut node.arguments, arguments, index); - transform_selection_set(&mut node.selection_set, arguments, index, false); - if add_alias { - add_alias_to_selection(node, index); - } + transform_selection_set(&mut node.selection_set, arguments, index); } executable::Selection::InlineFragment(node) => { let node = node.make_mut(); - transform_selection_set(&mut node.selection_set, arguments, index, false); + transform_selection_set(&mut node.selection_set, arguments, index); } _ => (), }); @@ -322,6 +343,7 @@ pub(crate) enum ContextBatchingError { NoSelectionSet, InvalidDocumentGenerated(WithErrors), InvalidRelativePath, + UnexpectedSelection, } #[cfg(test)] @@ -400,7 +422,7 @@ mod subgraph_context_unit_tests { hash_set.insert("two".to_string()); hash_set.insert("param".to_string()); let mut clone = selection_set.clone(); - transform_selection_set(&mut clone, &hash_set, 7, false); + transform_selection_set(&mut clone, &hash_set, 7); assert_eq!( "{ f(param: $variable) }", clone.serialize().no_indent().to_string() @@ -409,18 +431,26 @@ mod subgraph_context_unit_tests { // add variable that will hit and cause a rewrite hash_set.insert("variable".to_string()); let mut clone = selection_set.clone(); - transform_selection_set(&mut clone, &hash_set, 7, false); + transform_selection_set(&mut clone, &hash_set, 7); assert_eq!( "{ f(param: $variable_7) }", clone.serialize().no_indent().to_string() ); // add_alias = true will add a "_3:" alias - let mut clone = selection_set.clone(); - transform_selection_set(&mut clone, &hash_set, 3, true); + let clone = selection_set.clone(); + let mut operation = Node::new(executable::Operation { + operation_type: executable::OperationType::Query, + name: None, + variables: vec![], + directives: ast::DirectiveList(vec![]), + selection_set: clone, + }); + let count = 3; + transform_operation(&mut operation, &hash_set, &count).unwrap(); assert_eq!( - "{ _3: f(param: $variable_3) }", - clone.serialize().no_indent().to_string() + "{ _0: f(param: $variable_0) _1: f(param: $variable_1) _2: f(param: $variable_2) }", + operation.serialize().no_indent().to_string() ); } } From 7c238768b049fc7380eabed6ed14307c59f99091 Mon Sep 17 00:00:00 2001 From: Chris Lenfest Date: Mon, 20 May 2024 01:06:30 -0500 Subject: [PATCH 54/69] fmt and linter --- .../src/query_planner/subgraph_context.rs | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/apollo-router/src/query_planner/subgraph_context.rs b/apollo-router/src/query_planner/subgraph_context.rs index 17210d6fd0..6f1e03c4ad 100644 --- a/apollo-router/src/query_planner/subgraph_context.rs +++ b/apollo-router/src/query_planner/subgraph_context.rs @@ -212,7 +212,7 @@ pub(crate) fn build_operation_with_aliasing( let parsed_document = subgraph_operation.as_parsed(subgraph_schema); let mut ed = ExecutableDocument::new(); - + // for every operation in the document, go ahead and transform even though it's likely that only one exists if let Ok(document) = parsed_document { if let Some(anonymous_op) = &document.anonymous_operation { @@ -226,7 +226,7 @@ pub(crate) fn build_operation_with_aliasing( transform_operation(&mut cloned, arguments, count)?; ed.insert_operation(cloned); } - + return ed .validate(subgraph_schema) .map_err(ContextBatchingError::InvalidDocumentGenerated); @@ -234,7 +234,11 @@ pub(crate) fn build_operation_with_aliasing( Err(ContextBatchingError::NoSelectionSet) } -fn transform_operation(operation: &mut Node, arguments: &HashSet, count: &usize) -> Result<(), ContextBatchingError> { +fn transform_operation( + operation: &mut Node, + arguments: &HashSet, + count: &usize, +) -> Result<(), ContextBatchingError> { let mut selections: Vec = vec![]; let mut new_variables: Vec> = vec![]; operation.variables.iter().for_each(|v| { @@ -251,7 +255,7 @@ fn transform_operation(operation: &mut Node, arguments: &HashSet, arguments: &HashSet { - if field_selection != None { + if field_selection.is_some() { // if we get here, there is more than one field selection, which should not be the case // at the top level of a _entities selection set return Err(ContextBatchingError::UnexpectedSelection); } field_selection = Some(f.clone()); - }, + } _ => { // again, if we get here, something is wrong. _entities selection sets should have just one field selection return Err(ContextBatchingError::UnexpectedSelection); } } - }; - + } + let field_selection = field_selection.ok_or(ContextBatchingError::UnexpectedSelection)?; - + for i in 0..*count { // If we are aliasing, we know that there is only one selection in the top level SelectionSet // it is a field selection for _entities, so it's ok to reach in and give it an alias let mut cloned = field_selection.clone(); let cfs = cloned.make_mut(); cfs.alias = Some(Name::new_unchecked(format!("_{}", i).into())); - + transform_field_arguments(&mut cfs.arguments, arguments, i); transform_selection_set(&mut cfs.selection_set, arguments, i); selections.push(Selection::Field(cloned)); From 323a5c76428e97060aadfe0c4e82c092a3057359 Mon Sep 17 00:00:00 2001 From: Chris Lenfest Date: Mon, 20 May 2024 09:26:03 -0500 Subject: [PATCH 55/69] accidently messed up test name --- apollo-router/tests/set_context.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apollo-router/tests/set_context.rs b/apollo-router/tests/set_context.rs index 7684bd85b1..bc5f44d2da 100644 --- a/apollo-router/tests/set_context.rs +++ b/apollo-router/tests/set_context.rs @@ -220,7 +220,7 @@ async fn test_set_context_with_null() { #[tokio::test(flavor = "multi_thread")] async fn test_set_context_type_mismatch() { static QUERY: &str = r#" - query Query_fetch_failure { + query Query_type_mismatch { t { id u { From 5abdfa8108763ef85579cb0cf99b6a27a19be9c1 Mon Sep 17 00:00:00 2001 From: o0Ignition0o Date: Tue, 21 May 2024 11:51:10 +0200 Subject: [PATCH 56/69] update snapshots --- .../query_plan/build_query_plan_tests.rs | 27 +++---- .../build_query_plan_tests/provides.rs | 79 +++++++++++-------- 2 files changed, 57 insertions(+), 49 deletions(-) diff --git a/apollo-federation/tests/query_plan/build_query_plan_tests.rs b/apollo-federation/tests/query_plan/build_query_plan_tests.rs index 4c8bf1f13b..1d765c1a2f 100644 --- a/apollo-federation/tests/query_plan/build_query_plan_tests.rs +++ b/apollo-federation/tests/query_plan/build_query_plan_tests.rs @@ -195,23 +195,22 @@ fn field_covariance_and_type_explosion() { } "#, @r###" - QueryPlan { - Fetch(service: "Subgraph1") { - { - dummy { + QueryPlan { + Fetch(service: "Subgraph1") { + { + dummy { + field { + __typename + ... on Object { + field { __typename - field { - __typename - ... on Object { - field { - __typename - } - } - } } } - }, + } } - "### + } + }, + } + "### ); } diff --git a/apollo-federation/tests/query_plan/build_query_plan_tests/provides.rs b/apollo-federation/tests/query_plan/build_query_plan_tests/provides.rs index 23ba713f10..b09afe5c1f 100644 --- a/apollo-federation/tests/query_plan/build_query_plan_tests/provides.rs +++ b/apollo-federation/tests/query_plan/build_query_plan_tests/provides.rs @@ -302,49 +302,58 @@ fn it_works_on_unions() { // This is our sanity check: we first query _without_ the provides // to make sure we _do_ need to go the the second subgraph. @r###" - QueryPlan { - Sequence { - Fetch(service: "Subgraph1") { + QueryPlan { + Sequence { + Fetch(service: "Subgraph1") { + { + noProvides { + ... on T1 { + __typename + id + } + ... on T2 { + __typename + id + a + } + } + } + }, + Parallel { + Flatten(path: "noProvides") { + Fetch(service: "Subgraph2") { { - noProvides { + ... on T2 { __typename - ... on T1 { - __typename - id - } - ... on T2 { - __typename - id - a - } + id + } + } => + { + ... on T2 { + b } } }, - Flatten(path: "noProvides") { - Fetch(service: "Subgraph2") { - { - ... on T1 { - __typename - id - } - ... on T2 { - __typename - id - } - } => - { - ... on T1 { - a - } - ... on T2 { - b - } + }, + Flatten(path: "noProvides") { + Fetch(service: "Subgraph2") { + { + ... on T1 { + __typename + id } - }, + } => + { + ... on T1 { + a + } + } }, }, - } - "### + }, + }, + } + "### ); // Ensuring that querying only `a` can be done with subgraph1 only when provided. From d0736f250796ab91a193d42f7b8f90215b3b5a21 Mon Sep 17 00:00:00 2001 From: o0Ignition0o Date: Tue, 21 May 2024 13:58:57 +0200 Subject: [PATCH 57/69] update snapshots --- .../build_query_plan_tests/provides.rs | 92 +++++++++---------- ...ts__integration__redis__query_planner.snap | 1 + 2 files changed, 46 insertions(+), 47 deletions(-) diff --git a/apollo-federation/tests/query_plan/build_query_plan_tests/provides.rs b/apollo-federation/tests/query_plan/build_query_plan_tests/provides.rs index b09afe5c1f..79cc960d0e 100644 --- a/apollo-federation/tests/query_plan/build_query_plan_tests/provides.rs +++ b/apollo-federation/tests/query_plan/build_query_plan_tests/provides.rs @@ -372,22 +372,21 @@ fn it_works_on_unions() { } "#, @r###" - QueryPlan { - Fetch(service: "Subgraph1") { - { - withProvidesForT1 { - __typename - ... on T1 { - a - } - ... on T2 { - a - } - } + QueryPlan { + Fetch(service: "Subgraph1") { + { + withProvidesForT1 { + ... on T1 { + a } - }, + ... on T2 { + a + } + } } - "### + }, + } + "### ); // But ensure that querying `b` still goes to subgraph2 if only a is provided. @@ -407,41 +406,40 @@ fn it_works_on_unions() { } "#, @r###" - QueryPlan { - Sequence { - Fetch(service: "Subgraph1") { - { - withProvidesForT1 { - __typename - ... on T1 { - a - } - ... on T2 { - __typename - id - a - } - } + QueryPlan { + Sequence { + Fetch(service: "Subgraph1") { + { + withProvidesForT1 { + ... on T1 { + a } - }, - Flatten(path: "withProvidesForT1") { - Fetch(service: "Subgraph2") { - { - ... on T2 { - __typename - id - } - } => - { - ... on T2 { - b - } - } - }, - }, + ... on T2 { + __typename + id + a + } + } + } + }, + Flatten(path: "withProvidesForT1") { + Fetch(service: "Subgraph2") { + { + ... on T2 { + __typename + id + } + } => + { + ... on T2 { + b + } + } }, - } - "### + }, + }, + } + "### ); // Lastly, if both are provided, ensures we only hit subgraph1. diff --git a/apollo-router/tests/integration/snapshots/integration_tests__integration__redis__query_planner.snap b/apollo-router/tests/integration/snapshots/integration_tests__integration__redis__query_planner.snap index 4714fe2240..f90305be82 100644 --- a/apollo-router/tests/integration/snapshots/integration_tests__integration__redis__query_planner.snap +++ b/apollo-router/tests/integration/snapshots/integration_tests__integration__redis__query_planner.snap @@ -12,6 +12,7 @@ expression: query_plan "id": null, "inputRewrites": null, "outputRewrites": null, + "contextRewrites": null, "schemaAwareHash": "121b9859eba2d8fa6dde0a54b6e3781274cf69f7ffb0af912e92c01c6bfff6ca", "authorization": { "is_authenticated": false, From aa75f822d1de10530295a3bd15c3bdc8f516a97c Mon Sep 17 00:00:00 2001 From: o0Ignition0o Date: Tue, 21 May 2024 17:52:43 +0200 Subject: [PATCH 58/69] the test doesnt panic anymore? --- apollo-federation/tests/query_plan/build_query_plan_tests.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/apollo-federation/tests/query_plan/build_query_plan_tests.rs b/apollo-federation/tests/query_plan/build_query_plan_tests.rs index 1d765c1a2f..0e56f58f87 100644 --- a/apollo-federation/tests/query_plan/build_query_plan_tests.rs +++ b/apollo-federation/tests/query_plan/build_query_plan_tests.rs @@ -153,8 +153,6 @@ fn pick_keys_that_minimize_fetches() { /// (more precisely, this force the query planner to _consider_ type explosion; the generated /// query plan still ends up not type-exploding in practice since as it's not necessary). #[test] -#[should_panic(expected = "snapshot assertion")] -// TODO: investigate this failure fn field_covariance_and_type_explosion() { let planner = planner!( Subgraph1: r#" From 9fe931d49d8ea8bb7c8c383c71bced058f04f1b7 Mon Sep 17 00:00:00 2001 From: o0Ignition0o Date: Tue, 21 May 2024 19:37:09 +0200 Subject: [PATCH 59/69] Set required number of threads for any integration test to 2 to prevent CI to run out of resources while running them. --- Cargo.toml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 134d321297..26e00e4ff5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,6 +31,15 @@ members = [ # Note that xtask is not in the workspace member because it relies on dependencies that are incompatible with the router. Notably hyperx but there are others. ] +# integration tests require a multi threaded runtime. +# nextest is sometimes a bit too agressive on parallelism, +# expecially when running integration tests +# https://nexte.st/book/threads-required +[[profile.default.overrides]] +filter = 'test(/^apollo-router::/)' +threads-required = 2 + + # this makes build scripts and proc macros faster to compile [profile.dev.build-override] # If you un-comment the next line with 1.60.0, compile fails on circle-ci linux From abb8b7332e060a05bddf8f5c05178e0d088b88a8 Mon Sep 17 00:00:00 2001 From: o0Ignition0o Date: Tue, 21 May 2024 19:38:05 +0200 Subject: [PATCH 60/69] lint --- Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 26e00e4ff5..111713fa5b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,7 +39,6 @@ members = [ filter = 'test(/^apollo-router::/)' threads-required = 2 - # this makes build scripts and proc macros faster to compile [profile.dev.build-override] # If you un-comment the next line with 1.60.0, compile fails on circle-ci linux From 55c51baab30469249a4ec9fe519c98cfc6d758b9 Mon Sep 17 00:00:00 2001 From: o0Ignition0o Date: Tue, 21 May 2024 19:44:44 +0200 Subject: [PATCH 61/69] override the dev profile for tests since there s no default --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 111713fa5b..2a13285021 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,7 +35,7 @@ members = [ # nextest is sometimes a bit too agressive on parallelism, # expecially when running integration tests # https://nexte.st/book/threads-required -[[profile.default.overrides]] +[[profile.dev.overrides]] filter = 'test(/^apollo-router::/)' threads-required = 2 From a64e3a26630b180e89324c5f3513eecce414bf88 Mon Sep 17 00:00:00 2001 From: o0Ignition0o Date: Tue, 21 May 2024 20:05:41 +0200 Subject: [PATCH 62/69] move the nexttest profile to apollo-router so xtask picks it up --- Cargo.toml | 8 -------- apollo-router/Cargo.toml | 8 ++++++++ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2a13285021..134d321297 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,14 +31,6 @@ members = [ # Note that xtask is not in the workspace member because it relies on dependencies that are incompatible with the router. Notably hyperx but there are others. ] -# integration tests require a multi threaded runtime. -# nextest is sometimes a bit too agressive on parallelism, -# expecially when running integration tests -# https://nexte.st/book/threads-required -[[profile.dev.overrides]] -filter = 'test(/^apollo-router::/)' -threads-required = 2 - # this makes build scripts and proc macros faster to compile [profile.dev.build-override] # If you un-comment the next line with 1.60.0, compile fails on circle-ci linux diff --git a/apollo-router/Cargo.toml b/apollo-router/Cargo.toml index c8f059cfcb..f96ca7d394 100644 --- a/apollo-router/Cargo.toml +++ b/apollo-router/Cargo.toml @@ -23,6 +23,14 @@ test = false # needed to link an executable for each individual doctest: doctest = false +# integration tests require a multi threaded runtime. +# nextest is sometimes a bit too agressive on parallelism, +# expecially when running integration tests +# https://nexte.st/book/threads-required +[[profile.dev.overrides]] +filter = 'test(/^apollo-router::/)' +threads-required = 2 + [features] default = ["global-allocator"] From e0a3daec163cd818518b5d6eada84fb6250a8973 Mon Sep 17 00:00:00 2001 From: o0Ignition0o Date: Tue, 21 May 2024 20:08:11 +0200 Subject: [PATCH 63/69] update the nextest threads-required to be less agressive with the CI resources --- .config/nextest.toml | 2 +- apollo-router/Cargo.toml | 8 -------- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/.config/nextest.toml b/.config/nextest.toml index f081bbc39a..f7d5c09a6a 100644 --- a/.config/nextest.toml +++ b/.config/nextest.toml @@ -21,4 +21,4 @@ path = "junit.xml" # This filter applies only to to the integration tests in the apollo-router package. [[profile.default.overrides]] filter = 'package(apollo-router) & kind(test)' -threads-required = 2 +threads-required = 4 diff --git a/apollo-router/Cargo.toml b/apollo-router/Cargo.toml index f96ca7d394..c8f059cfcb 100644 --- a/apollo-router/Cargo.toml +++ b/apollo-router/Cargo.toml @@ -23,14 +23,6 @@ test = false # needed to link an executable for each individual doctest: doctest = false -# integration tests require a multi threaded runtime. -# nextest is sometimes a bit too agressive on parallelism, -# expecially when running integration tests -# https://nexte.st/book/threads-required -[[profile.dev.overrides]] -filter = 'test(/^apollo-router::/)' -threads-required = 2 - [features] default = ["global-allocator"] From 4f330c6ff0d74fddb6544799487007864e12f8f6 Mon Sep 17 00:00:00 2001 From: o0Ignition0o Date: Tue, 21 May 2024 20:18:20 +0200 Subject: [PATCH 64/69] change the override so it applies to the CI profile --- .config/nextest.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.config/nextest.toml b/.config/nextest.toml index f7d5c09a6a..9c243a577c 100644 --- a/.config/nextest.toml +++ b/.config/nextest.toml @@ -19,6 +19,6 @@ path = "junit.xml" # Integration tests require more than one thread. The default setting of 1 will cause too many integration tests to run # at the same time and causes tests to fail where timing is involved. # This filter applies only to to the integration tests in the apollo-router package. -[[profile.default.overrides]] -filter = 'package(apollo-router) & kind(test)' -threads-required = 4 +[[profile.ci.overrides]] +filter = 'test(/^apollo-router::/)' +threads-required = 2 From 2ca8a9a9dbedd57f93798943cfa1ff748aa4ead5 Mon Sep 17 00:00:00 2001 From: o0Ignition0o Date: Tue, 21 May 2024 20:34:42 +0200 Subject: [PATCH 65/69] ok we re making progress it seems --- .config/nextest.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.config/nextest.toml b/.config/nextest.toml index 9c243a577c..df8bab66e6 100644 --- a/.config/nextest.toml +++ b/.config/nextest.toml @@ -21,4 +21,4 @@ path = "junit.xml" # This filter applies only to to the integration tests in the apollo-router package. [[profile.ci.overrides]] filter = 'test(/^apollo-router::/)' -threads-required = 2 +threads-required = 4 From b29b4f69c4f0b34cd6283fa6ab0da4d168e4f136 Mon Sep 17 00:00:00 2001 From: Chris Lenfest Date: Wed, 22 May 2024 21:57:26 -0500 Subject: [PATCH 66/69] bump router-bridge version --- apollo-router/Cargo.toml | 2 +- fuzz/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apollo-router/Cargo.toml b/apollo-router/Cargo.toml index c8f059cfcb..577c29ae1e 100644 --- a/apollo-router/Cargo.toml +++ b/apollo-router/Cargo.toml @@ -188,7 +188,7 @@ regex = "1.10.3" reqwest.workspace = true # note: this dependency should _always_ be pinned, prefix the version with an `=` -router-bridge = "=0.5.23+v2.8.0-alpha.0" +router-bridge = "=0.5.23+v2.8.0-alpha.1" rust-embed = "8.2.0" rustls = "0.21.11" diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index 0708d9b467..42faa3f7da 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -20,7 +20,7 @@ reqwest = { workspace = true, features = ["json", "blocking"] } serde_json.workspace = true tokio.workspace = true # note: this dependency should _always_ be pinned, prefix the version with an `=` -router-bridge = "=0.5.23+v2.8.0-alpha.0" +router-bridge = "=0.5.23+v2.8.0-alpha.1" [dev-dependencies] anyhow = "1" From df344c7d1ad3b883e7ef7914854f105006e0d24b Mon Sep 17 00:00:00 2001 From: Chris Lenfest Date: Wed, 22 May 2024 22:48:52 -0500 Subject: [PATCH 67/69] update redis cache key --- apollo-router/tests/integration/redis.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/apollo-router/tests/integration/redis.rs b/apollo-router/tests/integration/redis.rs index 60519bc8c1..bd17cb5689 100644 --- a/apollo-router/tests/integration/redis.rs +++ b/apollo-router/tests/integration/redis.rs @@ -28,7 +28,7 @@ async fn query_planner() -> Result<(), BoxError> { // 2. run `docker compose up -d` and connect to the redis container by running `docker-compose exec redis /bin/bash`. // 3. Run the `redis-cli` command from the shell and start the redis `monitor` command. // 4. Run this test and yank the updated cache key from the redis logs. - let known_cache_key = "plan:0:v2.8.0-alpha.0:16385ebef77959fcdc520ad507eb1f7f7df28f1d54a0569e3adabcb4cd00d7ce:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:9c26cb1f820a78848ba3d5d3295c16aa971368c5295422fd33cc19d4a6006a9c"; + let known_cache_key = "plan:0:v2.8.0-alpha.1:a9e605fa09adc5a4b824e690b4de6f160d47d84ede5956b58a7d300cca1f7204:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:1910d63916aae7a1066cb8c7d622fc3a8e363ed1b6ac8e214deed4046abae85c"; let config = RedisConfig::from_url("redis://127.0.0.1:6379").unwrap(); let client = RedisClient::new(config, None, None, None); @@ -902,7 +902,7 @@ async fn connection_failure_blocks_startup() { async fn query_planner_redis_update_query_fragments() { test_redis_query_plan_config_update( include_str!("fixtures/query_planner_redis_config_update_query_fragments.router.yaml"), - "plan:0:v2.8.0-alpha.0:a9e605fa09adc5a4b824e690b4de6f160d47d84ede5956b58a7d300cca1f7204:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:ae8b525534cb7446a34715fc80edd41d4d29aa65c5f39f9237d4ed8459e3fe82", + "plan:0:v2.8.0-alpha.1:a9e605fa09adc5a4b824e690b4de6f160d47d84ede5956b58a7d300cca1f7204:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:ae8b525534cb7446a34715fc80edd41d4d29aa65c5f39f9237d4ed8459e3fe82", ) .await; } @@ -921,7 +921,7 @@ async fn query_planner_redis_update_planner_mode() { async fn query_planner_redis_update_introspection() { test_redis_query_plan_config_update( include_str!("fixtures/query_planner_redis_config_update_introspection.router.yaml"), - "plan:0:v2.8.0-alpha.0:a9e605fa09adc5a4b824e690b4de6f160d47d84ede5956b58a7d300cca1f7204:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:1910d63916aae7a1066cb8c7d622fc3a8e363ed1b6ac8e214deed4046abae85c", + "plan:0:v2.8.0-alpha.1:a9e605fa09adc5a4b824e690b4de6f160d47d84ede5956b58a7d300cca1f7204:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:1910d63916aae7a1066cb8c7d622fc3a8e363ed1b6ac8e214deed4046abae85c", ) .await; } @@ -930,7 +930,7 @@ async fn query_planner_redis_update_introspection() { async fn query_planner_redis_update_defer() { test_redis_query_plan_config_update( include_str!("fixtures/query_planner_redis_config_update_defer.router.yaml"), - "plan:0:v2.8.0-alpha.0:a9e605fa09adc5a4b824e690b4de6f160d47d84ede5956b58a7d300cca1f7204:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:8a17c5b196af5e3a18d24596424e9849d198f456dd48297b852a5f2ca847169b", + "plan:0:v2.8.0-alpha.1:a9e605fa09adc5a4b824e690b4de6f160d47d84ede5956b58a7d300cca1f7204:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:8a17c5b196af5e3a18d24596424e9849d198f456dd48297b852a5f2ca847169b", ) .await; } @@ -941,7 +941,7 @@ async fn query_planner_redis_update_type_conditional_fetching() { include_str!( "fixtures/query_planner_redis_config_update_type_conditional_fetching.router.yaml" ), - "plan:0:v2.8.0-alpha.0:a9e605fa09adc5a4b824e690b4de6f160d47d84ede5956b58a7d300cca1f7204:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:275f78612ed3d45cdf6bf328ef83e368b5a44393bd8c944d4a7d694aed61f017", + "plan:0:v2.8.0-alpha.1:a9e605fa09adc5a4b824e690b4de6f160d47d84ede5956b58a7d300cca1f7204:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:275f78612ed3d45cdf6bf328ef83e368b5a44393bd8c944d4a7d694aed61f017", ) .await; } @@ -952,7 +952,7 @@ async fn query_planner_redis_update_reuse_query_fragments() { include_str!( "fixtures/query_planner_redis_config_update_reuse_query_fragments.router.yaml" ), - "plan:0:v2.8.0-alpha.0:a9e605fa09adc5a4b824e690b4de6f160d47d84ede5956b58a7d300cca1f7204:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:15fbb62c94e8da6ea78f28a6eb86a615dcaf27ff6fd0748fac4eb614b0b17662", + "plan:0:v2.8.0-alpha.1:a9e605fa09adc5a4b824e690b4de6f160d47d84ede5956b58a7d300cca1f7204:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:15fbb62c94e8da6ea78f28a6eb86a615dcaf27ff6fd0748fac4eb614b0b17662", ) .await; } @@ -972,7 +972,7 @@ async fn test_redis_query_plan_config_update(updated_config: &str, new_cache_key router.assert_started().await; router.clear_redis_cache().await; - let starting_key = "plan:0:v2.8.0-alpha.0:a9e605fa09adc5a4b824e690b4de6f160d47d84ede5956b58a7d300cca1f7204:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:1910d63916aae7a1066cb8c7d622fc3a8e363ed1b6ac8e214deed4046abae85c"; + let starting_key = "plan:0:v2.8.0-alpha.1:a9e605fa09adc5a4b824e690b4de6f160d47d84ede5956b58a7d300cca1f7204:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:1910d63916aae7a1066cb8c7d622fc3a8e363ed1b6ac8e214deed4046abae85c"; router.execute_default_query().await; router.assert_redis_cache_contains(starting_key, None).await; router.update_config(updated_config).await; From 9613cf6acafa626258b688ac246aa77e7828a01e Mon Sep 17 00:00:00 2001 From: o0Ignition0o Date: Thu, 23 May 2024 11:16:29 +0200 Subject: [PATCH 68/69] bump bridge dependency --- Cargo.lock | 652 ++++++++++++++--------- apollo-router/Cargo.toml | 2 +- apollo-router/tests/integration/redis.rs | 2 +- fuzz/Cargo.toml | 2 +- 4 files changed, 395 insertions(+), 263 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3523470116..4b039c23aa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -48,14 +48,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" dependencies = [ "crypto-common", - "generic-array", + "generic-array 0.14.7", ] [[package]] name = "aes" -version = "0.8.3" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2" +checksum = "433cfd6710c9986c576a25ca913c39d66a6474107b406f34f91d4a8923395241" dependencies = [ "cfg-if", "cipher", @@ -298,7 +298,7 @@ dependencies = [ "diff", "directories", "displaydoc", - "ecdsa", + "ecdsa 0.16.9", "flate2", "fred", "futures", @@ -351,7 +351,7 @@ dependencies = [ "opentelemetry-zipkin", "opentelemetry_api", "opentelemetry_sdk 0.20.0", - "p256", + "p256 0.13.2", "parking_lot", "paste", "pin-project-lite", @@ -1080,7 +1080,7 @@ version = "1.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897f1db4020ad91f2c2421945ec49b7e3eb81cc3fea99e8b5dd5be721e697fed" dependencies = [ - "base64-simd 0.8.0", + "base64-simd", "bytes", "bytes-utils", "futures-core", @@ -1187,6 +1187,12 @@ dependencies = [ "rustc-demangle", ] +[[package]] +name = "base16ct" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" + [[package]] name = "base16ct" version = "0.2.0" @@ -1205,22 +1211,13 @@ version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" -[[package]] -name = "base64-simd" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "781dd20c3aff0bd194fe7d2a977dd92f21c173891f3a03b677359e5fa457e5d5" -dependencies = [ - "simd-abstraction", -] - [[package]] name = "base64-simd" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "339abbe78e73178762e23bea9dfd08e697eb3f3301cd4be981c0f78ba5859195" dependencies = [ - "outref 0.5.1", + "outref", "vsimd", ] @@ -1239,15 +1236,6 @@ dependencies = [ "serde", ] -[[package]] -name = "bincode" -version = "1.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" -dependencies = [ - "serde", -] - [[package]] name = "bit-set" version = "0.5.3" @@ -1275,25 +1263,13 @@ version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" -[[package]] -name = "bitvec" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" -dependencies = [ - "funty", - "radium", - "tap", - "wyz", -] - [[package]] name = "block-buffer" version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ - "generic-array", + "generic-array 0.14.7", ] [[package]] @@ -1302,7 +1278,7 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" dependencies = [ - "generic-array", + "generic-array 0.14.7", ] [[package]] @@ -1574,6 +1550,15 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" +[[package]] +name = "cmake" +version = "0.1.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31c789563b815f77f4250caee12365734369f942439b7defd71e18a48197130" +dependencies = [ + "cc", +] + [[package]] name = "colorchoice" version = "1.0.0" @@ -1716,12 +1701,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" -[[package]] -name = "cooked-waker" -version = "5.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147be55d677052dabc6b22252d5dd0fd4c29c8c27aa4f2fbef0f94aa003b406f" - [[package]] name = "cookie" version = "0.18.0" @@ -1892,13 +1871,25 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" +[[package]] +name = "crypto-bigint" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" +dependencies = [ + "generic-array 0.14.7", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + [[package]] name = "crypto-bigint" version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" dependencies = [ - "generic-array", + "generic-array 0.14.7", "rand_core 0.6.4", "subtle", "zeroize", @@ -1910,7 +1901,7 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ - "generic-array", + "generic-array 0.14.7", "rand_core 0.6.4", "typenum", ] @@ -1934,6 +1925,19 @@ dependencies = [ "cipher", ] +[[package]] +name = "curve25519-dalek" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a9b85542f99a2dfa2a1b8e192662741c9859a846b296bef1c92ef9b58b5a216" +dependencies = [ + "byteorder", + "digest 0.8.1", + "rand_core 0.5.1", + "subtle", + "zeroize", +] + [[package]] name = "curve25519-dalek" version = "4.1.2" @@ -2020,33 +2024,52 @@ dependencies = [ "uuid", ] +[[package]] +name = "deno-proc-macro-rules" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c65c2ffdafc1564565200967edc4851c7b55422d3913466688907efd05ea26f" +dependencies = [ + "deno-proc-macro-rules-macros", + "proc-macro2 1.0.76", + "syn 2.0.48", +] + +[[package]] +name = "deno-proc-macro-rules-macros" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3047b312b7451e3190865713a4dd6e1f821aed614ada219766ebc3024a690435" +dependencies = [ + "once_cell", + "proc-macro2 1.0.76", + "quote 1.0.35", + "syn 2.0.48", +] + [[package]] name = "deno_console" -version = "0.151.0" +version = "0.115.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1657ea36f527a5fa3b3d9c1ad9b9f0c9c0b263f966079f5aa0c7c09ff6f4e2e" +checksum = "19ab05b798826985966deb29fc6773ed29570de2f2147a30c4289c7cdf635214" dependencies = [ "deno_core", ] [[package]] name = "deno_core" -version = "0.280.0" +version = "0.200.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12d26f2d3e243bbbdd0851ab542b20ec48ac1fcf6c64ab06e81133da3113ebdd" +checksum = "a8ba264b90ceb6e95b39d82e674d8ecae86ca012f900338ea50d1a077d9d75fd" dependencies = [ "anyhow", - "bincode", - "bit-set", - "bit-vec", "bytes", - "cooked-waker", - "deno_core_icudata", "deno_ops", - "deno_unsync", "futures", + "indexmap 1.9.3", "libc", - "memoffset 0.9.0", + "log", + "once_cell", "parking_lot", "pin-project", "serde", @@ -2054,49 +2077,42 @@ dependencies = [ "serde_v8", "smallvec", "sourcemap", - "static_assertions", "tokio", "url", "v8", ] -[[package]] -name = "deno_core_icudata" -version = "0.0.73" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a13951ea98c0a4c372f162d669193b4c9d991512de9f2381dd161027f34b26b1" - [[package]] name = "deno_crypto" -version = "0.165.0" +version = "0.129.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f399d4bd84b014f5dcf9f0ceda39d7fc5624c732fb2c71f93dea4af2b5daa706" +checksum = "7247d39660238354f71103f0db7a944c3ac3fb6759ab5410d708f7496eaa89a5" dependencies = [ "aes", "aes-gcm", "aes-kw", - "base64 0.21.7", + "base64 0.13.1", "cbc", "const-oid", "ctr", - "curve25519-dalek", + "curve25519-dalek 2.1.3", "deno_core", "deno_web", - "elliptic-curve", + "elliptic-curve 0.12.3", "num-traits", "once_cell", - "p256", + "p256 0.11.1", "p384", - "p521", "rand 0.8.5", - "ring 0.17.5", + "ring 0.16.20", "rsa", + "sec1 0.3.0", "serde", "serde_bytes", "sha1", "sha2", - "signature", - "spki", + "signature 1.6.4", + "spki 0.6.0", "tokio", "uuid", "x25519-dalek", @@ -2104,33 +2120,30 @@ dependencies = [ [[package]] name = "deno_ops" -version = "0.156.0" +version = "0.78.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8237b272db1a6cb941b8a5a63ba63539004a8263e8b0230a11136d76eea273f9" +checksum = "ffd1c83b1fd465ee0156f2917c9af9ca09fe2bf54052a2cae1a8dcbc7b89aefc" dependencies = [ - "proc-macro-rules", + "deno-proc-macro-rules", + "lazy-regex", + "once_cell", + "pmutil", + "proc-macro-crate", "proc-macro2 1.0.76", "quote 1.0.35", + "regex", "strum 0.25.0", "strum_macros 0.25.3", + "syn 1.0.109", "syn 2.0.48", "thiserror", ] -[[package]] -name = "deno_unsync" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3d79c7af81e0a5ac75cff7b2fff4d1896e2bff694c688258edf21ef8a519736" -dependencies = [ - "tokio", -] - [[package]] name = "deno_url" -version = "0.151.0" +version = "0.115.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6babc9a52e441a08f9c4f03309fe80f1f7294a4c227bbddf064b3e1c62abbf0" +checksum = "20490fff3b0f8c176a815e26371ff23313ea7f39cd51057701524c5b6fc36f6c" dependencies = [ "deno_core", "serde", @@ -2139,17 +2152,15 @@ dependencies = [ [[package]] name = "deno_web" -version = "0.182.0" +version = "0.146.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86ce3a0a97a69379188214d92e03c22437f96f068d37686d1cd1eff34e4517a8" +checksum = "1dc8dda6e1337d4739ae9e94d75521689824d82a7deb154a2972b6eedac64507" dependencies = [ "async-trait", - "base64-simd 0.8.0", - "bytes", + "base64-simd", "deno_core", "encoding_rs", "flate2", - "futures", "serde", "tokio", "uuid", @@ -2158,13 +2169,24 @@ dependencies = [ [[package]] name = "deno_webidl" -version = "0.151.0" +version = "0.115.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ffa30c6acba42d8a4d952514bbb7b5d247b9c900fa1fd82ce9084c32f640827" +checksum = "73159d81053ead02e938b46d4bb7224c8e7cf25273ac16a250fb45bb09af7635" dependencies = [ "deno_core", ] +[[package]] +name = "der" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" +dependencies = [ + "const-oid", + "pem-rfc7468 0.6.0", + "zeroize", +] + [[package]] name = "der" version = "0.7.8" @@ -2172,7 +2194,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" dependencies = [ "const-oid", - "pem-rfc7468", + "pem-rfc7468 0.7.0", "zeroize", ] @@ -2262,6 +2284,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" +[[package]] +name = "digest" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" +dependencies = [ + "generic-array 0.12.4", +] + [[package]] name = "digest" version = "0.10.7" @@ -2366,18 +2397,30 @@ version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbfc4744c1b8f2a09adc0e55242f60b1af195d88596bd8700be74418c056c555" +[[package]] +name = "ecdsa" +version = "0.14.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" +dependencies = [ + "der 0.6.1", + "elliptic-curve 0.12.3", + "rfc6979 0.3.1", + "signature 1.6.4", +] + [[package]] name = "ecdsa" version = "0.16.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" dependencies = [ - "der", - "digest", - "elliptic-curve", - "rfc6979", - "signature", - "spki", + "der 0.7.8", + "digest 0.10.7", + "elliptic-curve 0.13.8", + "rfc6979 0.4.0", + "signature 2.2.0", + "spki 0.7.2", ] [[package]] @@ -2386,23 +2429,44 @@ version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +[[package]] +name = "elliptic-curve" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" +dependencies = [ + "base16ct 0.1.1", + "crypto-bigint 0.4.9", + "der 0.6.1", + "digest 0.10.7", + "ff 0.12.1", + "generic-array 0.14.7", + "group 0.12.1", + "hkdf", + "pem-rfc7468 0.6.0", + "pkcs8 0.9.0", + "rand_core 0.6.4", + "sec1 0.3.0", + "subtle", + "zeroize", +] + [[package]] name = "elliptic-curve" version = "0.13.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" dependencies = [ - "base16ct", - "crypto-bigint", - "digest", - "ff", - "generic-array", - "group", - "hkdf", - "pem-rfc7468", - "pkcs8", + "base16ct 0.2.0", + "crypto-bigint 0.5.5", + "digest 0.10.7", + "ff 0.13.0", + "generic-array 0.14.7", + "group 0.13.0", + "pem-rfc7468 0.7.0", + "pkcs8 0.10.2", "rand_core 0.6.4", - "sec1", + "sec1 0.7.3", "subtle", "zeroize", ] @@ -2415,9 +2479,9 @@ checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" [[package]] name = "encoding_rs" -version = "0.8.33" +version = "0.8.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" +checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b" dependencies = [ "cfg-if", ] @@ -2573,6 +2637,16 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" +[[package]] +name = "ff" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + [[package]] name = "ff" version = "0.13.0" @@ -2614,6 +2688,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" dependencies = [ "crc32fast", + "libz-ng-sys", "miniz_oxide", ] @@ -2751,20 +2826,14 @@ dependencies = [ [[package]] name = "fslock" -version = "0.2.1" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04412b8935272e3a9bae6f48c7bfff74c2911f60525404edfdd28e49884c3bfb" +checksum = "57eafdd0c16f57161105ae1b98a1238f97645f2f588438b2949c99a2af9616bf" dependencies = [ "libc", "winapi", ] -[[package]] -name = "funty" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" - [[package]] name = "futures" version = "0.3.30" @@ -2908,6 +2977,15 @@ dependencies = [ "slab", ] +[[package]] +name = "generic-array" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd" +dependencies = [ + "typenum", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -3076,22 +3154,24 @@ dependencies = [ [[package]] name = "group" -version = "0.13.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" dependencies = [ - "ff", + "ff 0.12.1", "rand_core 0.6.4", "subtle", ] [[package]] -name = "gzip-header" -version = "1.0.0" +name = "group" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95cc527b92e6029a62960ad99aa8a6660faa4555fe5f731aab13aa6a921795a2" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" dependencies = [ - "crc32fast", + "ff 0.13.0", + "rand_core 0.6.4", + "subtle", ] [[package]] @@ -3238,7 +3318,7 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "digest", + "digest 0.10.7", ] [[package]] @@ -3526,7 +3606,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" dependencies = [ "block-padding", - "generic-array", + "generic-array 0.14.7", ] [[package]] @@ -3777,6 +3857,29 @@ dependencies = [ "log", ] +[[package]] +name = "lazy-regex" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff63c423c68ea6814b7da9e88ce585f793c87ddd9e78f646970891769c8235d4" +dependencies = [ + "lazy-regex-proc_macros", + "once_cell", + "regex", +] + +[[package]] +name = "lazy-regex-proc_macros" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8edfc11b8f56ce85e207e62ea21557cfa09bb24a8f6b04ae181b086ff8611c22" +dependencies = [ + "proc-macro2 1.0.76", + "quote 1.0.35", + "regex", + "syn 1.0.109", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -3849,6 +3952,16 @@ dependencies = [ "threadpool", ] +[[package]] +name = "libz-ng-sys" +version = "1.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6409efc61b12687963e602df8ecf70e8ddacf95bc6576bcf16e3ac6328083c5" +dependencies = [ + "cmake", + "libc", +] + [[package]] name = "libz-sys" version = "1.1.12" @@ -4669,12 +4782,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "outref" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f222829ae9293e33a9f5e9f440c6760a3d450a64affe1846486b140db81c1f4" - [[package]] name = "outref" version = "0.5.1" @@ -4689,39 +4796,35 @@ checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" [[package]] name = "p256" -version = "0.13.2" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +checksum = "51f44edd08f51e2ade572f141051021c5af22677e42b7dd28a88155151c33594" dependencies = [ - "ecdsa", - "elliptic-curve", - "primeorder", + "ecdsa 0.14.8", + "elliptic-curve 0.12.3", "sha2", ] [[package]] -name = "p384" -version = "0.13.0" +name = "p256" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70786f51bcc69f6a4c0360e063a4cac5419ef7c5cd5b3c99ad70f3be5ba79209" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" dependencies = [ - "ecdsa", - "elliptic-curve", + "ecdsa 0.16.9", + "elliptic-curve 0.13.8", "primeorder", "sha2", ] [[package]] -name = "p521" -version = "0.13.3" +name = "p384" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fc9e2161f1f215afdfce23677034ae137bbd45016a880c2eb3ba8eb95f085b2" +checksum = "dfc8c5bf642dde52bb9e87c0ecd8ca5a76faac2eeed98dedb7c717997e1080aa" dependencies = [ - "base16ct", - "ecdsa", - "elliptic-curve", - "primeorder", - "rand_core 0.6.4", + "ecdsa 0.14.8", + "elliptic-curve 0.12.3", "sha2", ] @@ -4770,6 +4873,15 @@ dependencies = [ "serde", ] +[[package]] +name = "pem-rfc7468" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d159833a9105500e0398934e205e0773f0b27529557134ecfc51c27646adac" +dependencies = [ + "base64ct", +] + [[package]] name = "pem-rfc7468" version = "0.7.0" @@ -4886,13 +4998,24 @@ dependencies = [ [[package]] name = "pkcs1" -version = "0.7.5" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" +checksum = "eff33bdbdfc54cc98a2eca766ebdec3e1b8fb7387523d5c9c9a2891da856f719" dependencies = [ - "der", - "pkcs8", - "spki", + "der 0.6.1", + "pkcs8 0.9.0", + "spki 0.6.0", + "zeroize", +] + +[[package]] +name = "pkcs8" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" +dependencies = [ + "der 0.6.1", + "spki 0.6.0", ] [[package]] @@ -4901,8 +5024,8 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" dependencies = [ - "der", - "spki", + "der 0.7.8", + "spki 0.7.2", ] [[package]] @@ -4945,6 +5068,17 @@ dependencies = [ "plotters-backend", ] +[[package]] +name = "pmutil" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52a40bc70c2c58040d2d8b167ba9a5ff59fc9dab7ad44771cfde3dcfde7a09c6" +dependencies = [ + "proc-macro2 1.0.76", + "quote 1.0.35", + "syn 2.0.48", +] + [[package]] name = "polling" version = "2.8.0" @@ -5051,30 +5185,17 @@ version = "0.13.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" dependencies = [ - "elliptic-curve", -] - -[[package]] -name = "proc-macro-rules" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07c277e4e643ef00c1233393c673f655e3672cf7eb3ba08a00bdd0ea59139b5f" -dependencies = [ - "proc-macro-rules-macros", - "proc-macro2 1.0.76", - "syn 2.0.48", + "elliptic-curve 0.13.8", ] [[package]] -name = "proc-macro-rules-macros" -version = "0.4.0" +name = "proc-macro-crate" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "207fffb0fe655d1d47f6af98cc2793405e85929bdbc420d685554ff07be27ac7" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" dependencies = [ "once_cell", - "proc-macro2 1.0.76", - "quote 1.0.35", - "syn 2.0.48", + "toml_edit 0.19.15", ] [[package]] @@ -5164,7 +5285,7 @@ dependencies = [ "regex", "syn 1.0.109", "tempfile", - "which 4.4.2", + "which", ] [[package]] @@ -5255,12 +5376,6 @@ dependencies = [ "proc-macro2 1.0.76", ] -[[package]] -name = "radium" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" - [[package]] name = "rand" version = "0.7.3" @@ -5509,6 +5624,17 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4389f1d5789befaf6029ebd9f7dac4af7f7e3d61b69d4f30e2ac02b57e7712b0" +[[package]] +name = "rfc6979" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" +dependencies = [ + "crypto-bigint 0.4.9", + "hmac", + "zeroize", +] + [[package]] name = "rfc6979" version = "0.4.0" @@ -5650,9 +5776,9 @@ dependencies = [ [[package]] name = "router-bridge" -version = "0.5.23+v2.8.0-alpha.0" +version = "0.5.24+v2.8.0-alpha.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6824bf0a836f1df5b4091ff83efeedbf2aa56ab29baaff840b8f9053563d5c24" +checksum = "4a4b92a40b68c797d2d624716d5671e80de578f00c5012af37f19c74b175566b" dependencies = [ "anyhow", "async-channel 1.9.0", @@ -5670,7 +5796,7 @@ dependencies = [ "tower", "tower-service", "tracing", - "which 4.4.2", + "which", ] [[package]] @@ -5712,20 +5838,21 @@ dependencies = [ [[package]] name = "rsa" -version = "0.9.5" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af6c4b23d99685a1408194da11270ef8e9809aff951cc70ec9b17350b087e474" +checksum = "094052d5470cbcef561cb848a7209968c9f12dfa6d668f4bca048ac5de51099c" dependencies = [ - "const-oid", - "digest", + "byteorder", + "digest 0.10.7", "num-bigint-dig", "num-integer", + "num-iter", "num-traits", "pkcs1", - "pkcs8", + "pkcs8 0.9.0", "rand_core 0.6.4", - "signature", - "spki", + "signature 1.6.4", + "smallvec", "subtle", "zeroize", ] @@ -5948,16 +6075,30 @@ dependencies = [ "untrusted 0.7.1", ] +[[package]] +name = "sec1" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" +dependencies = [ + "base16ct 0.1.1", + "der 0.6.1", + "generic-array 0.14.7", + "pkcs8 0.9.0", + "subtle", + "zeroize", +] + [[package]] name = "sec1" version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" dependencies = [ - "base16ct", - "der", - "generic-array", - "pkcs8", + "base16ct 0.2.0", + "der 0.7.8", + "generic-array 0.14.7", + "pkcs8 0.10.2", "subtle", "zeroize", ] @@ -6126,12 +6267,15 @@ dependencies = [ [[package]] name = "serde_v8" -version = "0.189.0" +version = "0.111.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "893c995255d6fbf55c33166b651fd037c4e3cc7864bf82213ea18d0ec94ed165" +checksum = "309b3060a9627882514f3a3ce3cc08ceb347a76aeeadc58f138c3f189cf88b71" dependencies = [ + "bytes", + "derive_more", "num-bigint", "serde", + "serde_bytes", "smallvec", "thiserror", "v8", @@ -6182,7 +6326,7 @@ checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ "cfg-if", "cpufeatures", - "digest", + "digest 0.10.7", ] [[package]] @@ -6193,7 +6337,7 @@ checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" dependencies = [ "cfg-if", "cpufeatures", - "digest", + "digest 0.10.7", ] [[package]] @@ -6231,21 +6375,22 @@ dependencies = [ [[package]] name = "signature" -version = "2.2.0" +version = "1.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" dependencies = [ - "digest", + "digest 0.10.7", "rand_core 0.6.4", ] [[package]] -name = "simd-abstraction" -version = "0.7.1" +name = "signature" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cadb29c57caadc51ff8346233b5cec1d240b68ce55cf1afc764818791876987" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" dependencies = [ - "outref 0.1.0", + "digest 0.10.7", + "rand_core 0.6.4", ] [[package]] @@ -6324,20 +6469,17 @@ dependencies = [ [[package]] name = "sourcemap" -version = "8.0.1" +version = "6.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "208d40b9e8cad9f93613778ea295ed8f3c2b1824217c6cfc7219d3f6f45b96d4" +checksum = "e4cbf65ca7dc576cf50e21f8d0712d96d4fcfd797389744b7b222a85cdf5bd90" dependencies = [ - "base64-simd 0.7.0", - "bitvec", "data-encoding", "debugid", "if_chain", - "rustc-hash", "rustc_version 0.2.3", "serde", "serde_json", - "unicode-id-start", + "unicode-id", "url", ] @@ -6353,6 +6495,16 @@ version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +[[package]] +name = "spki" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" +dependencies = [ + "base64ct", + "der 0.6.1", +] + [[package]] name = "spki" version = "0.7.2" @@ -6360,7 +6512,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d1e996ef02c474957d681f1b05213dfb0abab947b446a62d37770b23500184a" dependencies = [ "base64ct", - "der", + "der 0.7.8", ] [[package]] @@ -6526,12 +6678,6 @@ dependencies = [ "libc", ] -[[package]] -name = "tap" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" - [[package]] name = "tempfile" version = "3.10.0" @@ -6910,7 +7056,7 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit", + "toml_edit 0.22.4", ] [[package]] @@ -6922,6 +7068,17 @@ dependencies = [ "serde", ] +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap 2.2.3", + "toml_datetime", + "winnow", +] + [[package]] name = "toml_edit" version = "0.22.4" @@ -7451,10 +7608,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] -name = "unicode-id-start" -version = "1.1.2" +name = "unicode-id" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8f73150333cb58412db36f2aca8f2875b013049705cc77b94ded70a1ab1f5da" +checksum = "b1b6def86329695390197b82c1e244a54a131ceb66c996f2088a3876e2ae083f" [[package]] name = "unicode-ident" @@ -7570,17 +7727,14 @@ dependencies = [ [[package]] name = "v8" -version = "0.91.1" +version = "0.74.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69026e2e8af55a4d2f20c0c17f690e8b31472bf76ab75b1205d3a0fab60c8f84" +checksum = "2eedac634b8dd39b889c5b62349cbc55913780226239166435c5cf66771792ea" dependencies = [ - "bitflags 2.4.0", + "bitflags 1.3.2", "fslock", - "gzip-header", - "home", - "miniz_oxide", "once_cell", - "which 5.0.0", + "which", ] [[package]] @@ -7763,19 +7917,6 @@ dependencies = [ "rustix 0.38.31", ] -[[package]] -name = "which" -version = "5.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bf3ea8596f3a0dd5980b46430f2058dfe2c36a27ccfbb1845d6fbfcd9ba6e14" -dependencies = [ - "either", - "home", - "once_cell", - "rustix 0.38.31", - "windows-sys 0.48.0", -] - [[package]] name = "widestring" version = "1.0.2" @@ -8058,22 +8199,13 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8dab7ac864710bdea6594becbea5b5050333cf34fefb0dc319567eb347950d4" -[[package]] -name = "wyz" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" -dependencies = [ - "tap", -] - [[package]] name = "x25519-dalek" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb66477291e7e8d2b0ff1bcb900bf29489a9692816d79874bea351e7a8b6de96" dependencies = [ - "curve25519-dalek", + "curve25519-dalek 4.1.2", "rand_core 0.6.4", "serde", "zeroize", diff --git a/apollo-router/Cargo.toml b/apollo-router/Cargo.toml index 577c29ae1e..943057738a 100644 --- a/apollo-router/Cargo.toml +++ b/apollo-router/Cargo.toml @@ -188,7 +188,7 @@ regex = "1.10.3" reqwest.workspace = true # note: this dependency should _always_ be pinned, prefix the version with an `=` -router-bridge = "=0.5.23+v2.8.0-alpha.1" +router-bridge = "=0.5.24+v2.8.0-alpha.1" rust-embed = "8.2.0" rustls = "0.21.11" diff --git a/apollo-router/tests/integration/redis.rs b/apollo-router/tests/integration/redis.rs index bd17cb5689..29d932c911 100644 --- a/apollo-router/tests/integration/redis.rs +++ b/apollo-router/tests/integration/redis.rs @@ -28,7 +28,7 @@ async fn query_planner() -> Result<(), BoxError> { // 2. run `docker compose up -d` and connect to the redis container by running `docker-compose exec redis /bin/bash`. // 3. Run the `redis-cli` command from the shell and start the redis `monitor` command. // 4. Run this test and yank the updated cache key from the redis logs. - let known_cache_key = "plan:0:v2.8.0-alpha.1:a9e605fa09adc5a4b824e690b4de6f160d47d84ede5956b58a7d300cca1f7204:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:1910d63916aae7a1066cb8c7d622fc3a8e363ed1b6ac8e214deed4046abae85c"; + let known_cache_key = "plan:0:v2.8.0-alpha.1:16385ebef77959fcdc520ad507eb1f7f7df28f1d54a0569e3adabcb4cd00d7ce:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:9c26cb1f820a78848ba3d5d3295c16aa971368c5295422fd33cc19d4a6006a9c"; let config = RedisConfig::from_url("redis://127.0.0.1:6379").unwrap(); let client = RedisClient::new(config, None, None, None); diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index 42faa3f7da..da27ae250f 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -20,7 +20,7 @@ reqwest = { workspace = true, features = ["json", "blocking"] } serde_json.workspace = true tokio.workspace = true # note: this dependency should _always_ be pinned, prefix the version with an `=` -router-bridge = "=0.5.23+v2.8.0-alpha.1" +router-bridge = "=0.5.24+v2.8.0-alpha.1" [dev-dependencies] anyhow = "1" From 1307fe2d7023f3824d2f73836b85269105012a31 Mon Sep 17 00:00:00 2001 From: o0Ignition0o Date: Thu, 23 May 2024 11:23:11 +0200 Subject: [PATCH 69/69] explicitly call out caches will be repopulated --- .changesets/feat_clenfest_set_context.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.changesets/feat_clenfest_set_context.md b/.changesets/feat_clenfest_set_context.md index 97d06149c0..025348f883 100644 --- a/.changesets/feat_clenfest_set_context.md +++ b/.changesets/feat_clenfest_set_context.md @@ -2,4 +2,6 @@ Adds the ability for the router to execute query plans with context rewrites on them. These are generated by the @fromContext directive and are used to map a Value in the collected data JSON onto a variable which will in turn be used as an argument to a field resolver. +⚠️ This ships with a new version of federation, which means distributed caches will be repopulated. + By [@clenfest](https://github.com/clenfest) in https://github.com/apollographql/router/pull/5097 \ No newline at end of file