Skip to content

Commit e27ac26

Browse files
authored
execute_introspection_only_query + [email protected] + [email protected] (#906)
* Add execute_introspection_only_query bypassing SchemaIntrospectionSplit * Prepare [email protected] and [email protected] releases
1 parent ee610a3 commit e27ac26

File tree

10 files changed

+101
-77
lines changed

10 files changed

+101
-77
lines changed

crates/apollo-compiler/CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
1717
## Maintenance
1818
## Documentation-->
1919

20-
# [x.x.x] (unreleased) - 2024-mm-dd
20+
# [1.0.0-beta.21](https://crates.io/crates/apollo-compiler/1.0.0-beta.21) - 2024-08-03
2121

2222
## BREAKING
2323

crates/apollo-compiler/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "apollo-compiler"
3-
version = "1.0.0-beta.20" # When bumping, also update README.md
3+
version = "1.0.0-beta.21" # When bumping, also update README.md
44
authors = ["Irina Shestak <[email protected]>"]
55
license = "MIT OR Apache-2.0"
66
repository = "https://github.com/apollographql/apollo-rs"

crates/apollo-compiler/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ Or add this to your `Cargo.toml` for a manual installation:
4040
# Just an example, change to the necessary package version.
4141
# Using an exact dependency is recommended for beta versions
4242
[dependencies]
43-
apollo-compiler = "=1.0.0-beta.20"
43+
apollo-compiler = "=1.0.0-beta.21"
4444
```
4545

4646
## Rust versions

crates/apollo-compiler/src/execution/introspection_execute.rs

Lines changed: 66 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,14 @@ use crate::executable::OperationType;
44
use crate::execution::engine::execute_selection_set;
55
use crate::execution::engine::ExecutionMode;
66
use crate::execution::resolver::ResolvedValue;
7+
use crate::execution::GraphQLError;
78
use crate::execution::JsonMap;
89
use crate::execution::Response;
910
use crate::execution::SchemaIntrospectionError;
1011
use crate::execution::SchemaIntrospectionSplit;
1112
use crate::schema;
1213
use crate::schema::Implementers;
1314
use crate::schema::Name;
14-
use crate::validation::SuspectedValidationBug;
1515
use crate::validation::Valid;
1616
use crate::ExecutableDocument;
1717
use crate::Node;
@@ -47,13 +47,12 @@ impl SchemaIntrospectionQuery {
4747
/// Generally [`split`][SchemaIntrospectionSplit::split] should be used instead.
4848
///
4949
/// [schema introspection]: https://spec.graphql.org/October2021/#sec-Schema-Introspection
50-
#[doc(hidden)]
51-
// Hidden to discourage usage, but pub to allow Router "both" mode to use legacy code
52-
// for deciding what is or isn’t an introspection query.
53-
pub fn assume_only_intropsection_fields(
50+
pub(crate) fn assume_only_intropsection_fields(
5451
document: Valid<ExecutableDocument>,
5552
) -> Result<Self, SchemaIntrospectionError> {
56-
super::introspection_max_depth::check_document(&document)?;
53+
let operation = document.operations.get(None).unwrap();
54+
super::introspection_max_depth::check_introspection_max_depth(&document, operation)
55+
.map_err(SchemaIntrospectionError::DeeplyNestedIntrospectionList)?;
5756
Ok(Self(document))
5857
}
5958

@@ -72,19 +71,17 @@ impl SchemaIntrospectionQuery {
7271
execute_non_introspection_parts: impl FnOnce(&Valid<ExecutableDocument>) -> Response,
7372
) -> Response {
7473
match SchemaIntrospectionSplit::split(schema, document, operation) {
75-
Ok(SchemaIntrospectionSplit::Only(introspection_query)) => introspection_query
76-
.execute(schema, variable_values)
77-
.unwrap_or_else(|err| err.into_response(&document.sources)),
74+
Ok(SchemaIntrospectionSplit::Only(introspection_query)) => {
75+
introspection_query.execute(schema, variable_values)
76+
}
7877
Ok(SchemaIntrospectionSplit::None) => execute_non_introspection_parts(document),
7978
Ok(SchemaIntrospectionSplit::Both {
8079
introspection_query,
8180
filtered_document: filtered_operation,
8281
}) => {
8382
let non_introspection_response =
8483
execute_non_introspection_parts(&filtered_operation);
85-
let introspection_response = introspection_query
86-
.execute(schema, variable_values)
87-
.unwrap_or_else(|err| err.into_response(&document.sources));
84+
let introspection_response = introspection_query.execute(schema, variable_values);
8885
non_introspection_response.merge(introspection_response)
8986
}
9087
Err(err) => err.into_response(&document.sources),
@@ -95,49 +92,65 @@ impl SchemaIntrospectionQuery {
9592
/// that can also be converted with [`into_response`][SuspectedValidationBug::into_response].
9693
///
9794
/// [request error]: https://spec.graphql.org/October2021/#sec-Errors.Request-errors
98-
pub fn execute(
99-
&self,
100-
schema: &Valid<Schema>,
101-
variable_values: &Valid<JsonMap>,
102-
) -> Result<Response, SuspectedValidationBug> {
95+
pub fn execute(&self, schema: &Valid<Schema>, variable_values: &Valid<JsonMap>) -> Response {
10396
let operation = self.0.operations.get(None).unwrap();
104-
debug_assert_eq!(operation.operation_type, OperationType::Query);
105-
106-
// https://spec.graphql.org/October2021/#sec-Query
107-
let object_type_name = operation.object_type();
108-
let object_type_def =
109-
schema
110-
.get_object(object_type_name)
111-
.ok_or_else(|| SuspectedValidationBug {
112-
message: format!(
113-
"Root operation type {object_type_name} is undefined or not an object type."
114-
),
115-
location: None,
116-
})?;
117-
let implementers_map = &OnceLock::new();
118-
let initial_value = &IntrospectionRootResolver(SchemaWithCache {
119-
schema,
120-
implementers_map,
121-
});
97+
execute_introspection_only_query(schema, &self.0, operation, variable_values)
98+
}
99+
}
122100

123-
let mut errors = Vec::new();
124-
let path = None;
125-
let data = execute_selection_set(
126-
schema,
127-
&self.0,
128-
variable_values,
129-
&mut errors,
130-
path,
131-
ExecutionMode::Normal,
132-
object_type_def,
133-
initial_value,
134-
&operation.selection_set.selections,
135-
);
136-
Ok(Response {
137-
data: data.into(),
138-
errors,
139-
extensions: Default::default(),
140-
})
101+
/// Execute a query whose [root fields][Operation::root_fields] are all intropsection meta-fields:
102+
/// `__schema`, `__type`, or `__typename`.
103+
///
104+
/// Returns an error if `operation_name` does not [correspond to a an operation definition](https://spec.graphql.org/October2021/#GetOperation())
105+
pub fn execute_introspection_only_query(
106+
schema: &Valid<Schema>,
107+
document: &Valid<ExecutableDocument>,
108+
operation: &Node<Operation>,
109+
variable_values: &Valid<JsonMap>,
110+
) -> Response {
111+
if operation.operation_type != OperationType::Query {
112+
return Response::from_request_error(GraphQLError::new(
113+
format!(
114+
"execute_introspection_only_query called with a {}",
115+
operation.operation_type.name()
116+
),
117+
operation.location(),
118+
&document.sources,
119+
));
120+
}
121+
122+
// https://spec.graphql.org/October2021/#sec-Query
123+
let object_type_name = operation.object_type();
124+
let Some(object_type_def) = schema.get_object(object_type_name) else {
125+
return Response::from_request_error(GraphQLError::new(
126+
"Undefined root operation type",
127+
object_type_name.location(),
128+
&document.sources,
129+
));
130+
};
131+
let implementers_map = &OnceLock::new();
132+
let initial_value = &IntrospectionRootResolver(SchemaWithCache {
133+
schema,
134+
implementers_map,
135+
});
136+
137+
let mut errors = Vec::new();
138+
let path = None;
139+
let data = execute_selection_set(
140+
schema,
141+
document,
142+
variable_values,
143+
&mut errors,
144+
path,
145+
ExecutionMode::Normal,
146+
object_type_def,
147+
initial_value,
148+
&operation.selection_set.selections,
149+
);
150+
Response {
151+
data: data.into(),
152+
errors,
153+
extensions: Default::default(),
141154
}
142155
}
143156

crates/apollo-compiler/src/execution/introspection_max_depth.rs

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,40 @@
1+
use crate::executable::Operation;
12
use crate::executable::Selection;
23
use crate::executable::SelectionSet;
3-
use crate::execution::introspection_split::get_fragment;
4-
use crate::execution::SchemaIntrospectionError;
4+
use crate::parser::SourceSpan;
55
use crate::validation::Valid;
66
use crate::ExecutableDocument;
77

88
const MAX_LISTS_DEPTH: u32 = 3;
99

10-
pub(crate) fn check_document(
10+
#[derive(Debug)]
11+
pub struct DeeplyNestedIntrospectionListError {
12+
pub location: Option<SourceSpan>,
13+
}
14+
15+
pub fn check_introspection_max_depth(
1116
document: &Valid<ExecutableDocument>,
12-
) -> Result<(), SchemaIntrospectionError> {
13-
for operation in document.operations.iter() {
14-
let initial_depth = 0;
15-
check_selection_set(document, initial_depth, &operation.selection_set)?;
16-
}
17-
Ok(())
17+
operation: &Operation,
18+
) -> Result<(), DeeplyNestedIntrospectionListError> {
19+
let initial_depth = 0;
20+
check_selection_set(document, initial_depth, &operation.selection_set)
1821
}
1922

2023
fn check_selection_set(
2124
document: &Valid<ExecutableDocument>,
2225
depth_so_far: u32,
2326
selection_set: &SelectionSet,
24-
) -> Result<(), SchemaIntrospectionError> {
27+
) -> Result<(), DeeplyNestedIntrospectionListError> {
2528
for selection in &selection_set.selections {
2629
match selection {
2730
Selection::InlineFragment(inline) => {
2831
check_selection_set(document, depth_so_far, &inline.selection_set)?
2932
}
3033
Selection::FragmentSpread(spread) => {
3134
// Validation ensures that `Valid<ExecutableDocument>` does not contain fragment cycles
32-
let def = get_fragment(document, &spread.fragment_name)?;
33-
check_selection_set(document, depth_so_far, &def.selection_set)?
35+
if let Some(def) = document.fragments.get(&spread.fragment_name) {
36+
check_selection_set(document, depth_so_far, &def.selection_set)?
37+
}
3438
}
3539
Selection::Field(field) => {
3640
let mut depth = depth_so_far;
@@ -40,9 +44,9 @@ fn check_selection_set(
4044
) {
4145
depth += 1;
4246
if depth >= MAX_LISTS_DEPTH {
43-
return Err(SchemaIntrospectionError::DeeplyNestedIntrospectionList(
44-
field.name.location(),
45-
));
47+
return Err(DeeplyNestedIntrospectionListError {
48+
location: field.name.location(),
49+
});
4650
}
4751
}
4852
check_selection_set(document, depth, &field.selection_set)?

crates/apollo-compiler/src/execution/introspection_split.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use crate::executable::Operation;
99
use crate::executable::OperationType;
1010
use crate::executable::Selection;
1111
use crate::executable::SelectionSet;
12+
use crate::execution::introspection_max_depth::DeeplyNestedIntrospectionListError;
1213
use crate::execution::GraphQLError;
1314
use crate::execution::Response;
1415
use crate::execution::SchemaIntrospectionQuery;
@@ -65,7 +66,7 @@ pub enum SchemaIntrospectionSplit {
6566
#[derive(Debug)]
6667
pub enum SchemaIntrospectionError {
6768
SuspectedValidationBug(SuspectedValidationBug),
68-
DeeplyNestedIntrospectionList(Option<SourceSpan>),
69+
DeeplyNestedIntrospectionList(DeeplyNestedIntrospectionListError),
6970
Unsupported {
7071
message: String,
7172
location: Option<SourceSpan>,
@@ -597,8 +598,8 @@ impl SchemaIntrospectionError {
597598
pub fn into_graphql_error(self, sources: &SourceMap) -> GraphQLError {
598599
match self {
599600
Self::SuspectedValidationBug(s) => s.into_graphql_error(sources),
600-
Self::DeeplyNestedIntrospectionList(location) => {
601-
GraphQLError::new("Maximum introspection depth exceeded", location, sources)
601+
Self::DeeplyNestedIntrospectionList(e) => {
602+
GraphQLError::new("Maximum introspection depth exceeded", e.location, sources)
602603
}
603604
Self::Unsupported { message, location } => {
604605
GraphQLError::new(message, location, sources)

crates/apollo-compiler/src/execution/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ mod result_coercion;
1616

1717
pub use self::input_coercion::coerce_variable_values;
1818
pub use self::input_coercion::InputCoercionError;
19+
pub use self::introspection_execute::execute_introspection_only_query;
1920
pub use self::introspection_execute::SchemaIntrospectionQuery;
21+
pub use self::introspection_max_depth::check_introspection_max_depth;
2022
pub use self::introspection_split::SchemaIntrospectionError;
2123
pub use self::introspection_split::SchemaIntrospectionSplit;
2224
pub use self::response::GraphQLError;

crates/apollo-smith/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
1818
## Maintenance
1919
2020
## Documentation -->
21+
# [0.11.0](https://crates.io/crates/apollo-smith/0.10.0) - 2024-09-03
22+
23+
- **Update apollo-compiler dependency to `=1.0.0-beta.21`**
24+
2125
# [0.10.0](https://crates.io/crates/apollo-smith/0.10.0) - 2024-07-31
2226

2327
- **Update apollo-parser dependency to `0.8.0`**

crates/apollo-smith/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "apollo-smith"
3-
version = "0.10.0" # When bumping, also update README.md
3+
version = "0.11.0" # When bumping, also update README.md
44
edition = "2021"
55
authors = ["Benjamin Coenen <[email protected]>"]
66
license = "MIT OR Apache-2.0"
@@ -22,7 +22,7 @@ categories = [
2222
]
2323

2424
[dependencies]
25-
apollo-compiler = { path = "../apollo-compiler", version = "=1.0.0-beta.20" }
25+
apollo-compiler = { path = "../apollo-compiler", version = "=1.0.0-beta.21" }
2626
apollo-parser = { path = "../apollo-parser", version = "0.8.0" }
2727
arbitrary = { version = "1.3.0", features = ["derive"] }
2828
indexmap = "2.0.0"

crates/apollo-smith/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ and add `apollo-smith` to your Cargo.toml:
4949
## fuzz/Cargo.toml
5050

5151
[dependencies]
52-
apollo-smith = "0.10.0"
52+
apollo-smith = "0.11.0"
5353
```
5454

5555
It can then be used in a `fuzz_target` along with the [`arbitrary`] crate,

0 commit comments

Comments
 (0)