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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions federation-2/router-bridge/src/introspect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,7 @@ fragment TypeRef on __Type {
}),
graphql_validation: true,
reuse_query_fragments: None,
generate_query_fragments: None,
debug: Default::default(),
},
)
Expand Down
85 changes: 85 additions & 0 deletions federation-2/router-bridge/src/planner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -677,6 +677,12 @@ pub struct QueryPlannerConfig {
/// planner's default.
pub reuse_query_fragments: Option<bool>,

/// If enabled, the query planner will extract inline fragments into
/// fragment definitions before sending queries to subgraphs. This can
/// significantly reduce the size of the query sent to subgraphs, but may
/// increase the time it takes to plan the query.
pub generate_query_fragments: Option<bool>,

/// A sub-set of configurations that are meant for debugging or testing. All the configurations in this
/// sub-set are provided without guarantees of stability (they may be dangerous) or continued support (they
/// may be removed without warning).
Expand All @@ -691,6 +697,7 @@ impl Default for QueryPlannerConfig {
}),
graphql_validation: true,
reuse_query_fragments: None,
generate_query_fragments: None,
debug: Default::default(),
}
}
Expand Down Expand Up @@ -764,6 +771,8 @@ mod tests {
const NO_OPERATION: &str = include_str!("testdata/no_operation.graphql");
const QUERY_REUSE_QUERY_FRAGMENTS: &str =
include_str!("testdata/query_reuse_query_fragments.graphql");
const QUERY_GENERATE_QUERY_FRAGMENTS: &str =
include_str!("testdata/query_generate_query_fragments.graphql");

const MULTIPLE_ANONYMOUS_QUERIES: &str =
include_str!("testdata/query_with_multiple_anonymous_operations.graphql");
Expand All @@ -773,6 +782,8 @@ mod tests {
include_str!("testdata/schema_without_review_body.graphql");
const SCHEMA_REUSE_QUERY_FRAGMENTS: &str =
include_str!("testdata/schema_reuse_query_fragments.graphql");
const SCHEMA_GENERATE_QUERY_FRAGMENTS: &str =
include_str!("testdata/schema_generate_query_fragments.graphql");
const CORE_IN_V0_1: &str = include_str!("testdata/core_in_v0.1.graphql");
const UNSUPPORTED_FEATURE: &str = include_str!("testdata/unsupported_feature.graphql");
const UNSUPPORTED_FEATURE_FOR_EXECUTION: &str =
Expand Down Expand Up @@ -970,6 +981,78 @@ mod tests {
insta::assert_snapshot!(serde_json::to_string_pretty(&payload.data).unwrap());
}

#[tokio::test]
async fn generate_query_fragments_defaults_to_false() {
let planner = Planner::<serde_json::Value>::new(
SCHEMA_GENERATE_QUERY_FRAGMENTS.to_string(),
QueryPlannerConfig::default(),
)
.await
.unwrap();

let payload = planner
.plan(
QUERY_GENERATE_QUERY_FRAGMENTS.to_string(),
None,
PlanOptions::default(),
)
.await
.unwrap()
.into_result()
.unwrap();
insta::assert_snapshot!(serde_json::to_string_pretty(&payload.data).unwrap());
}

#[tokio::test]
async fn generate_query_fragments_explicit_false() {
let planner = Planner::<serde_json::Value>::new(
SCHEMA_GENERATE_QUERY_FRAGMENTS.to_string(),
QueryPlannerConfig {
generate_query_fragments: Some(false),
..Default::default()
},
)
.await
.unwrap();

let payload = planner
.plan(
QUERY_GENERATE_QUERY_FRAGMENTS.to_string(),
None,
PlanOptions::default(),
)
.await
.unwrap()
.into_result()
.unwrap();
insta::assert_snapshot!(serde_json::to_string_pretty(&payload.data).unwrap());
}

#[tokio::test]
async fn generate_query_fragments_true() {
let planner = Planner::<serde_json::Value>::new(
SCHEMA_GENERATE_QUERY_FRAGMENTS.to_string(),
QueryPlannerConfig {
generate_query_fragments: Some(true),
..Default::default()
},
)
.await
.unwrap();

let payload = planner
.plan(
QUERY_GENERATE_QUERY_FRAGMENTS.to_string(),
None,
PlanOptions::default(),
)
.await
.unwrap()
.into_result()
.unwrap();
insta::assert_snapshot!(serde_json::to_string_pretty(&payload.data).unwrap());
}

#[tokio::test]
async fn parse_errors_return_the_right_usage_reporting_data() {
let planner =
Expand Down Expand Up @@ -2034,6 +2117,7 @@ feature https://specs.apollo.dev/unsupported-feature/v0.1 is for: SECURITY but i
}),
graphql_validation: true,
reuse_query_fragments: None,
generate_query_fragments: None,
debug: Default::default(),
},
)
Expand Down Expand Up @@ -2113,6 +2197,7 @@ feature https://specs.apollo.dev/unsupported-feature/v0.1 is for: SECURITY but i
}),
graphql_validation: true,
reuse_query_fragments: None,
generate_query_fragments: None,
debug: Default::default(),
},
)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
source: router-bridge/src/planner.rs
assertion_line: 1003
expression: "serde_json::to_string_pretty(&payload.data).unwrap()"
---
{
"queryPlan": {
"kind": "QueryPlan",
"node": {
"kind": "Fetch",
"serviceName": "Subgraph1",
"variableUsages": [],
"operation": "{t{__typename ...on A{x y t{__typename ...on A{x y}...on B{z}}}}}",
"operationKind": "query"
}
},
"formattedQueryPlan": "QueryPlan {\n Fetch(service: \"Subgraph1\") {\n {\n t {\n __typename\n ... on A {\n x\n y\n t {\n __typename\n ... on A {\n x\n y\n }\n ... on B {\n z\n }\n }\n }\n }\n }\n },\n}"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
source: router-bridge/src/planner.rs
assertion_line: 1028
expression: "serde_json::to_string_pretty(&payload.data).unwrap()"
---
{
"queryPlan": {
"kind": "QueryPlan",
"node": {
"kind": "Fetch",
"serviceName": "Subgraph1",
"variableUsages": [],
"operation": "{t{__typename ...on A{x y t{__typename ...on A{x y}...on B{z}}}}}",
"operationKind": "query"
}
},
"formattedQueryPlan": "QueryPlan {\n Fetch(service: \"Subgraph1\") {\n {\n t {\n __typename\n ... on A {\n x\n y\n t {\n __typename\n ... on A {\n x\n y\n }\n ... on B {\n z\n }\n }\n }\n }\n }\n },\n}"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
source: router-bridge/src/planner.rs
assertion_line: 1054
expression: "serde_json::to_string_pretty(&payload.data).unwrap()"
---
{
"queryPlan": {
"kind": "QueryPlan",
"node": {
"kind": "Fetch",
"serviceName": "Subgraph1",
"variableUsages": [],
"operation": "{t{__typename ..._generated_onA3_0}}fragment _generated_onA2_0 on A{x y}fragment _generated_onA3_0 on A{x y t{__typename ..._generated_onA2_0 ...on B{z}}}",
"operationKind": "query"
}
},
"formattedQueryPlan": "QueryPlan {\n Fetch(service: \"Subgraph1\") {\n {\n t {\n __typename\n ..._generated_onA3_0\n }\n }\n \n fragment _generated_onA2_0 on A {\n x\n y\n }\n \n fragment _generated_onA3_0 on A {\n x\n y\n t {\n __typename\n ..._generated_onA2_0\n ... on B {\n z\n }\n }\n }\n },\n}"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
query {
t {
... on A {
x
y
t {
... on A {
x
y
}
... on B {
z
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
schema
@link(url: "https://specs.apollo.dev/link/v1.0")
@link(url: "https://specs.apollo.dev/join/v0.4", for: EXECUTION) {
query: Query
}

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
) 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

type A @join__type(graph: SUBGRAPH1) {
x: Int
y: Int
t: T
}

type B @join__type(graph: SUBGRAPH1) {
z: Int
}

scalar join__DirectiveArguments

scalar join__FieldSet

enum join__Graph {
SUBGRAPH1 @join__graph(name: "Subgraph1", url: "")
}

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) {
t: T
t2: T
}

union T
@join__type(graph: SUBGRAPH1)
@join__unionMember(graph: SUBGRAPH1, member: "A")
@join__unionMember(graph: SUBGRAPH1, member: "B") =
A
| B