Skip to content

Commit

Permalink
Post-processing of response data using apollo-parser (#86)
Browse files Browse the repository at this point in the history
Fixes #44
Related #72
  • Loading branch information
cecton committed Nov 5, 2021
1 parent 3de1a12 commit 61b263d
Show file tree
Hide file tree
Showing 9 changed files with 443 additions and 28 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

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

2 changes: 1 addition & 1 deletion crates/apollo-router-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ license-file = "./LICENSE"
failfast = []

[dependencies]
apollo-parser = { git = "https://github.com/apollographql/apollo-rs.git", rev = "0e845624d8f93f9c2f228a456941f07007b60dfc" }
apollo-parser = { git = "https://github.com/apollographql/apollo-rs.git", rev = "14bb84337a8bacd5cd27d7d7df429936f104b63b" }
async-trait = "0.1.51"
derivative = "2.2.0"
displaydoc = "0.2"
Expand Down
8 changes: 5 additions & 3 deletions crates/apollo-router-core/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,10 +179,12 @@ impl From<JoinError> for QueryPlannerError {
QueryPlannerError::JoinError(Arc::new(err))
}
}
#[derive(Debug, Error)]

/// Error in the schema.
#[derive(Debug, Error, Display)]
pub enum SchemaError {
#[error(transparent)]
/// IO error: {0}
IoError(#[from] std::io::Error),
#[error("Parsing error(s)")]
/// Parsing error(s).
ParseErrors(Vec<apollo_parser::Error>),
}
18 changes: 10 additions & 8 deletions crates/apollo-router-core/src/federated.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ impl Fetcher for FederatedGraph {
let plan = {
match query_planner
.get(
request.query.to_owned(),
request.query.as_str().to_owned(),
request.operation_name.to_owned(),
Default::default(),
)
Expand Down Expand Up @@ -165,17 +165,22 @@ impl Fetcher for FederatedGraph {
Arc::clone(&response),
&root,
&plan,
request,
request.clone(),
Arc::clone(&service_registry),
Arc::clone(&schema),
)
.instrument(query_execution_span)
.await;

// TODO: this is not great but there is no other way
Arc::try_unwrap(response)
let mut response = Arc::try_unwrap(response)
.expect("todo: how to prove?")
.into_inner()
.into_inner();

tracing::debug_span!("format_response")
.in_scope(|| request.query.format_response(&mut response));

response
}
.with_current_subscriber(),
)
Expand Down Expand Up @@ -243,11 +248,8 @@ fn execute<'a>(
serde_json::to_string_pretty(&response.lock().await.data).unwrap();
tracing::trace!("New data:\n{}", received,);
}
#[cfg_attr(feature = "failfast", allow(unreachable_code))]
Err(err) => {
#[cfg(feature = "failfast")]
panic!("failfast: {}", err);
tracing::error!("Fetch error: {}", err);
failfast_error!("Fetch error: {}", err);
response
.lock()
.await
Expand Down
64 changes: 64 additions & 0 deletions crates/apollo-router-core/src/json_ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ pub trait ValueExt {
#[track_caller]
fn deep_merge(&mut self, other: &Self);

/// Returns `true` if the values are equal and the objects are ordered the same.
///
/// **Note:** this is recursive.
fn eq_and_ordered(&self, other: &Self) -> bool;

/// Returns `true` if the set is a subset of another, i.e., `other` contains at least all the
/// values in `self`.
#[track_caller]
Expand Down Expand Up @@ -145,6 +150,46 @@ impl ValueExt for Value {
}
}

fn eq_and_ordered(&self, other: &Self) -> bool {
match (self, other) {
(Value::Object(a), Value::Object(b)) => {
let mut it_a = a.iter();
let mut it_b = b.iter();

loop {
match (it_a.next(), it_b.next()) {
(Some(_), None) | (None, Some(_)) => break false,
(None, None) => break true,
(Some((field_a, value_a)), Some((field_b, value_b)))
if field_a == field_b && ValueExt::eq_and_ordered(value_a, value_b) =>
{
continue
}
(Some(_), Some(_)) => break false,
}
}
}
(Value::Array(a), Value::Array(b)) => {
let mut it_a = a.iter();
let mut it_b = b.iter();

loop {
match (it_a.next(), it_b.next()) {
(Some(_), None) | (None, Some(_)) => break false,
(None, None) => break true,
(Some(value_a), Some(value_b))
if ValueExt::eq_and_ordered(value_a, value_b) =>
{
continue
}
(Some(_), Some(_)) => break false,
}
}
}
(a, b) => a == b,
}
}

fn is_subset(&self, superset: &Value) -> bool {
match (self, superset) {
(Value::Object(subset), Value::Object(superset)) => {
Expand Down Expand Up @@ -452,4 +497,23 @@ mod tests {
json!({"obj":{"arr":[{"prop1":1},{"prop4":4}]}}),
);
}

#[test]
fn eq_and_ordered() {
// test not objects
assert!(json!([1, 2, 3]).eq_and_ordered(&json!([1, 2, 3])));
assert!(!json!([1, 3, 2]).eq_and_ordered(&json!([1, 2, 3])));

// test objects not nested
assert!(json!({"foo":1,"bar":2}).eq_and_ordered(&json!({"foo":1,"bar":2})));
assert!(!json!({"foo":1,"bar":2}).eq_and_ordered(&json!({"foo":1,"bar":3})));
assert!(!json!({"foo":1,"bar":2}).eq_and_ordered(&json!({"foo":1,"bar":2,"baz":3})));
assert!(!json!({"foo":1,"bar":2,"baz":3}).eq_and_ordered(&json!({"foo":1,"bar":2})));
assert!(!json!({"bar":2,"foo":1}).eq_and_ordered(&json!({"foo":1,"bar":2})));

// test objects nested
assert!(json!({"baz":{"foo":1,"bar":2}}).eq_and_ordered(&json!({"baz":{"foo":1,"bar":2}})));
assert!(!json!({"baz":{"bar":2,"foo":1}}).eq_and_ordered(&json!({"baz":{"foo":1,"bar":2}})));
assert!(!json!([1,{"bar":2,"foo":1},2]).eq_and_ordered(&json!([1,{"foo":1,"bar":2},2])));
}
}
24 changes: 24 additions & 0 deletions crates/apollo-router-core/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,27 @@
#![cfg_attr(feature = "failfast", allow(unreachable_code))]

macro_rules! failfast_debug {
($($tokens:tt)+) => {{
tracing::debug!($($tokens)+);
#[cfg(feature = "failfast")]
panic!(
"failfast triggered. \
Please remove the feature failfast if you don't want to see these panics"
);
}};
}

macro_rules! failfast_error {
($($tokens:tt)+) => {{
tracing::error!($($tokens)+);
#[cfg(feature = "failfast")]
panic!(
"failfast triggered. \
Please remove the feature failfast if you don't want to see these panics"
);
}};
}

mod error;
mod federated;
mod json_ext;
Expand Down
10 changes: 5 additions & 5 deletions crates/apollo-router-core/src/naive_introspection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,12 @@ static KNOWN_INTROSPECTION_QUERIES: Lazy<Vec<String>> = Lazy::new(|| {
/// A cache containing our well known introspection queries.
#[derive(Debug)]
pub struct NaiveIntrospection {
cache: HashMap<String, serde_json::Value>,
cache: HashMap<Query, serde_json::Value>,
}

impl NaiveIntrospection {
#[cfg(test)]
pub fn from_cache(cache: HashMap<String, serde_json::Value>) -> Self {
pub fn from_cache(cache: HashMap<Query, serde_json::Value>) -> Self {
Self { cache }
}

Expand All @@ -53,7 +53,7 @@ impl NaiveIntrospection {
.iter()
.zip(responses)
.filter_map(|(cache_key, response)| match response.into_result() {
Ok(introspection_value) => Some((cache_key.clone(), introspection_value)),
Ok(introspection_value) => Some((cache_key.into(), introspection_value)),
Err(e) => {
let errors = e
.iter()
Expand Down Expand Up @@ -84,10 +84,10 @@ mod naive_introspection_tests {

#[test]
fn test_plan() {
let query_to_test = "this is a test query".to_string();
let query_to_test = "this is a test query";
let expected_data = serde_json::Value::Number(42.into());

let cache = [(query_to_test.clone(), expected_data.clone())]
let cache = [(query_to_test.into(), expected_data.clone())]
.iter()
.cloned()
.collect();
Expand Down

0 comments on commit 61b263d

Please sign in to comment.