Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make the executor and validation APIs public to enable split parsing and execution #874

Merged
merged 7 commits into from
Apr 3, 2021
1 change: 1 addition & 0 deletions integration_tests/juniper_tests/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ publish = false
derive_more = "0.99"
futures = "0.3"
juniper = { path = "../../juniper" }
juniper_subscriptions = { path = "../../juniper_subscriptions" }

[dev-dependencies]
async-trait = "0.1.39"
Expand Down
2 changes: 2 additions & 0 deletions integration_tests/juniper_tests/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,5 @@ mod issue_398;
mod issue_407;
#[cfg(test)]
mod issue_500;
#[cfg(test)]
mod pre_parse;
102 changes: 102 additions & 0 deletions integration_tests/juniper_tests/src/pre_parse.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
use futures::{Stream, StreamExt, TryFutureExt};
use juniper::{
executor::{execute_validated_query_async, get_operation, resolve_validated_subscription},
graphql_object, graphql_subscription,
parser::parse_document_source,
validation::{validate_input_values, visit_all_rules, ValidatorContext},
EmptyMutation, FieldError, OperationType, RootNode, Variables,
};
use std::pin::Pin;

pub struct Context;

impl juniper::Context for Context {}

pub type UserStream = Pin<Box<dyn Stream<Item = Result<User, FieldError>> + Send>>;

pub struct Query;

#[graphql_object(context = Context)]
impl Query {
fn users() -> Vec<User> {
vec![User]
}
}

pub struct Subscription;

#[graphql_subscription(context = Context)]
impl Subscription {
async fn users() -> UserStream {
Box::pin(futures::stream::iter(vec![Ok(User)]))
}
}

#[derive(Clone)]
pub struct User;

#[graphql_object(context = Context)]
impl User {
fn id() -> i32 {
1
}
}

type Schema = RootNode<'static, Query, EmptyMutation<Context>, Subscription>;

#[tokio::test]
async fn query_document_can_be_pre_parsed() {
let root_node = &Schema::new(Query, EmptyMutation::<Context>::new(), Subscription);

let document_source = r#"query { users { id } }"#;
let document = parse_document_source(document_source, &root_node.schema).unwrap();

{
let mut ctx = ValidatorContext::new(&root_node.schema, &document);
visit_all_rules(&mut ctx, &document);
let errors = ctx.into_errors();
assert!(errors.is_empty());
}

let operation = get_operation(&document, None).unwrap();
assert!(operation.item.operation_type == OperationType::Query);

let errors = validate_input_values(&juniper::Variables::new(), operation, &root_node.schema);
assert!(errors.is_empty());

let (_, errors) = execute_validated_query_async(
&document,
operation,
root_node,
&Variables::new(),
&Context {},
)
.await
.unwrap();

assert!(errors.len() == 0);
}

#[tokio::test]
async fn subscription_document_can_be_pre_parsed() {
let root_node = &Schema::new(Query, EmptyMutation::<Context>::new(), Subscription);

let document_source = r#"subscription { users { id } }"#;
let document = parse_document_source(document_source, &root_node.schema).unwrap();

let operation = get_operation(&document, None).unwrap();
assert!(operation.item.operation_type == OperationType::Subscription);

let mut stream = resolve_validated_subscription(
&document,
&operation,
&root_node,
&Variables::new(),
&Context {},
)
.map_ok(|(stream, errors)| juniper_subscriptions::Connection::from_stream(stream, errors))
.await
.unwrap();

let _ = stream.next().await.unwrap();
}
4 changes: 4 additions & 0 deletions juniper/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,13 +112,15 @@ pub struct Directive<'a, S> {
pub arguments: Option<Spanning<Arguments<'a, S>>>,
}

#[allow(missing_docs)]
#[derive(Clone, PartialEq, Debug)]
pub enum OperationType {
Query,
Mutation,
Subscription,
}

#[allow(missing_docs)]
#[derive(Clone, PartialEq, Debug)]
pub struct Operation<'a, S> {
pub operation_type: OperationType,
Expand All @@ -136,12 +138,14 @@ pub struct Fragment<'a, S> {
pub selection_set: Vec<Selection<'a, S>>,
}

#[doc(hidden)]
#[derive(Clone, PartialEq, Debug)]
pub enum Definition<'a, S> {
Operation(Spanning<Operation<'a, S>>),
Fragment(Spanning<Fragment<'a, S>>),
}

#[doc(hidden)]
pub type Document<'a, S> = Vec<Definition<'a, S>>;

/// Parse an unstructured input value into a Rust data type.
Expand Down
1 change: 1 addition & 0 deletions juniper/src/executor/look_ahead.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ where
}
}

#[doc(hidden)]
#[derive(Debug, Clone, PartialEq)]
pub struct ChildSelection<'a, S: 'a> {
pub(super) inner: LookAheadSelection<'a, S>,
Expand Down
4 changes: 4 additions & 0 deletions juniper/src/executor/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//! Resolve the document to values

use std::{
borrow::Cow,
cmp::Ordering,
Expand Down Expand Up @@ -54,6 +56,7 @@ pub struct Registry<'r, S = DefaultScalarValue> {
pub types: FnvHashMap<Name, MetaType<'r, S>>,
}

#[allow(missing_docs)]
#[derive(Clone)]
pub enum FieldPath<'a> {
Root(SourcePosition),
Expand Down Expand Up @@ -979,6 +982,7 @@ where
Ok((value, errors))
}

#[doc(hidden)]
pub fn get_operation<'b, 'd, 'e, S>(
document: &'b Document<'d, S>,
operation_name: Option<&str>,
Expand Down
12 changes: 8 additions & 4 deletions juniper/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,13 +119,13 @@ mod value;
#[macro_use]
mod macros;
mod ast;
mod executor;
pub mod executor;
mod introspection;
pub mod parser;
pub(crate) mod schema;
mod types;
mod util;
mod validation;
pub mod validation;
// This needs to be public until docs have support for private modules:
// https://github.com/rust-lang/cargo/issues/1520
pub mod http;
Expand All @@ -145,12 +145,15 @@ pub use crate::util::to_camel_case;
use crate::{
executor::{execute_validated_query, get_operation},
introspection::{INTROSPECTION_QUERY, INTROSPECTION_QUERY_WITHOUT_DESCRIPTIONS},
parser::{parse_document_source, ParseError, Spanning},
parser::parse_document_source,
validation::{validate_input_values, visit_all_rules, ValidatorContext},
};

pub use crate::{
ast::{FromInputValue, InputValue, Selection, ToInputValue, Type},
ast::{
Definition, Document, FromInputValue, InputValue, Operation, OperationType, Selection,
ToInputValue, Type,
},
executor::{
Applies, Context, ExecutionError, ExecutionResult, Executor, FieldError, FieldResult,
FromContext, IntoFieldError, IntoResolvable, LookAheadArgument, LookAheadMethods,
Expand All @@ -161,6 +164,7 @@ pub use crate::{
subscription::{ExtractTypeFromStream, IntoFieldResult},
AsDynGraphQLValue,
},
parser::{ParseError, Spanning},
schema::{
meta,
model::{RootNode, SchemaType},
Expand Down
1 change: 1 addition & 0 deletions juniper/src/validation/input_value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ enum Path<'a> {
ObjectField(&'a str, &'a Path<'a>),
}

#[doc(hidden)]
pub fn validate_input_values<S>(
values: &Variables<S>,
operation: &Spanning<Operation<S>>,
Expand Down
2 changes: 1 addition & 1 deletion juniper/src/validation/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ mod visitor;
#[cfg(test)]
pub(crate) mod test_harness;

pub(crate) use self::rules::visit_all_rules;
pub use self::{
context::{RuleError, ValidatorContext},
input_value::validate_input_values,
multi_visitor::MultiVisitorNil,
rules::visit_all_rules,
traits::Visitor,
visitor::visit,
};
Expand Down
1 change: 1 addition & 0 deletions juniper/src/validation/multi_visitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use crate::{
#[doc(hidden)]
pub struct MultiVisitorNil;

#[doc(hidden)]
impl MultiVisitorNil {
pub fn with<V>(self, visitor: V) -> MultiVisitorCons<V, Self> {
MultiVisitorCons(visitor, self)
Expand Down
3 changes: 2 additions & 1 deletion juniper/src/validation/rules/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ use crate::{
};
use std::fmt::Debug;

pub(crate) fn visit_all_rules<'a, S: Debug>(ctx: &mut ValidatorContext<'a, S>, doc: &'a Document<S>)
#[doc(hidden)]
pub fn visit_all_rules<'a, S: Debug>(ctx: &mut ValidatorContext<'a, S>, doc: &'a Document<S>)
where
S: ScalarValue,
{
Expand Down