From 071e2b9a35563e7e59297b457e5c505daa8a5eb0 Mon Sep 17 00:00:00 2001 From: Miroslav Zoricak Date: Sun, 4 Oct 2020 20:28:11 +0200 Subject: [PATCH] feat: add parse and validate functions Split out parse and validate and then re-use them in all execute functions. --- juniper/src/lib.rs | 176 ++++++++++++++++++++++++++++----------------- 1 file changed, 109 insertions(+), 67 deletions(-) diff --git a/juniper/src/lib.rs b/juniper/src/lib.rs index 154ab67c7..c9798ef83 100644 --- a/juniper/src/lib.rs +++ b/juniper/src/lib.rs @@ -168,7 +168,7 @@ use crate::{ }; pub use crate::{ - ast::{FromInputValue, InputValue, Selection, ToInputValue, Type}, + ast::{Document, FromInputValue, InputValue, Operation, Selection, ToInputValue, Type}, executor::{ Applies, Context, ExecutionError, ExecutionResult, Executor, FieldError, FieldResult, FromContext, IntoFieldError, IntoResolvable, LookAheadArgument, LookAheadMethods, @@ -228,43 +228,113 @@ impl<'a> fmt::Display for GraphQLError<'a> { impl<'a> std::error::Error for GraphQLError<'a> {} -/// Execute a query synchronously in a provided schema -pub fn execute_sync<'a, S, QueryT, MutationT, SubscriptionT>( +pub fn parse<'a, S, QueryT, MutationT, SubscriptionT>( document_source: &'a str, - operation_name: Option<&str>, - root_node: &'a RootNode, - variables: &Variables, - context: &QueryT::Context, -) -> Result<(Value, Vec>), GraphQLError<'a>> + root_node: &'a RootNode<'a, QueryT, MutationT, SubscriptionT, S>, +) -> Result, Spanning>> where S: ScalarValue, QueryT: GraphQLType, MutationT: GraphQLType, SubscriptionT: GraphQLType, { - let document = parse_document_source(document_source, &root_node.schema)?; + parse_document_source(document_source, &root_node.schema) +} - { - let mut ctx = ValidatorContext::new(&root_node.schema, &document); - visit_all_rules(&mut ctx, &document); +pub struct ValidDocument<'a, S, QueryT, MutationT, SubscriptionT> +where + S: ScalarValue, + QueryT: GraphQLType, + MutationT: GraphQLType, + SubscriptionT: GraphQLType, +{ + document: &'a Document<'a, S>, + root_node: &'a RootNode<'a, QueryT, MutationT, SubscriptionT, S>, + variables: Variables, + operation: &'a Spanning>, +} - let errors = ctx.into_errors(); - if !errors.is_empty() { - return Err(GraphQLError::ValidationError(errors)); - } +pub fn validate<'a, 'e, S, QueryT, MutationT, SubscriptionT>( + document: &'a Document<'a, S>, + root_node: &'a RootNode<'a, QueryT, MutationT, SubscriptionT, S>, + operation_name: Option<&str>, + variables: Variables, +) -> Result, GraphQLError<'e>> +where + S: ScalarValue, + QueryT: GraphQLType, + MutationT: GraphQLType, + SubscriptionT: GraphQLType, +{ + let mut ctx = ValidatorContext::new(&root_node.schema, &document); + visit_all_rules(&mut ctx, &document); + + let errors = ctx.into_errors(); + if !errors.is_empty() { + return Err(GraphQLError::ValidationError(errors)); } let operation = get_operation(&document, operation_name)?; { - let errors = validate_input_values(variables, operation, &root_node.schema); + let errors = validate_input_values(&variables, operation, &root_node.schema); if !errors.is_empty() { return Err(GraphQLError::ValidationError(errors)); } } - execute_validated_query(&document, operation, root_node, variables, context) + let doc = ValidDocument { + document, + root_node, + variables, + operation, + }; + Ok(doc) +} + +pub fn execute_valid_sync<'a, S, QueryT, MutationT, SubscriptionT>( + doc: &'a ValidDocument<'a, S, QueryT, MutationT, SubscriptionT>, + context: &QueryT::Context, +) -> Result<(Value, Vec>), GraphQLError<'a>> +where + S: ScalarValue, + QueryT: GraphQLType, + MutationT: GraphQLType, + SubscriptionT: GraphQLType, +{ + execute_validated_query( + &doc.document, + &doc.operation, + &doc.root_node, + &doc.variables, + context, + ) +} + +/// Execute a query synchronously in a provided schema +pub fn execute_sync<'a, S, QueryT, MutationT, SubscriptionT>( + document_source: &'a str, + operation_name: Option<&str>, + root_node: &'a RootNode, + variables: &Variables, + context: &QueryT::Context, +) -> Result<(Value, Vec>), GraphQLError<'a>> +where + S: ScalarValue, + QueryT: GraphQLType, + MutationT: GraphQLType, + SubscriptionT: GraphQLType, +{ + let doc: Document<'a, S> = parse(&document_source, &root_node)?; + let valid_doc = validate(&doc, &root_node, operation_name, variables.to_owned())?; + execute_validated_query( + &valid_doc.document, + &valid_doc.operation, + valid_doc.root_node, + &valid_doc.variables, + context, + ) } /// Execute a query in a provided schema @@ -285,30 +355,16 @@ where SubscriptionT::TypeInfo: Sync, S: ScalarValue + Send + Sync, { - let document = parse_document_source(document_source, &root_node.schema)?; - - { - let mut ctx = ValidatorContext::new(&root_node.schema, &document); - visit_all_rules(&mut ctx, &document); - - let errors = ctx.into_errors(); - if !errors.is_empty() { - return Err(GraphQLError::ValidationError(errors)); - } - } - - let operation = get_operation(&document, operation_name)?; - - { - let errors = validate_input_values(variables, operation, &root_node.schema); - - if !errors.is_empty() { - return Err(GraphQLError::ValidationError(errors)); - } - } - - executor::execute_validated_query_async(&document, operation, root_node, variables, context) - .await + let doc: Document<'a, S> = parse(&document_source, &root_node)?; + let valid_doc = validate(&doc, &root_node, operation_name, variables.to_owned())?; + executor::execute_validated_query_async( + &valid_doc.document, + &valid_doc.operation, + valid_doc.root_node, + &valid_doc.variables, + context, + ) + .await } /// Resolve subscription into `ValuesStream` @@ -329,31 +385,17 @@ where SubscriptionT::TypeInfo: Sync, S: ScalarValue + Send + Sync, { - let document: crate::ast::Document<'a, S> = - parse_document_source(document_source, &root_node.schema)?; - - { - let mut ctx = ValidatorContext::new(&root_node.schema, &document); - visit_all_rules(&mut ctx, &document); - - let errors = ctx.into_errors(); - if !errors.is_empty() { - return Err(GraphQLError::ValidationError(errors)); - } - } - - let operation = get_operation(&document, operation_name)?; - - { - let errors = validate_input_values(&variables, operation, &root_node.schema); - - if !errors.is_empty() { - return Err(GraphQLError::ValidationError(errors)); - } - } - - executor::resolve_validated_subscription(&document, operation, root_node, variables, context) - .await + let doc: Document<'a, S> = parse(&document_source, &root_node)?; + let valid_doc: ValidDocument<'a, S, QueryT, MutationT, SubscriptionT> = + validate(&doc, &root_node, operation_name, variables.to_owned())?; + executor::resolve_validated_subscription( + &valid_doc.document, + &valid_doc.operation, + valid_doc.root_node, + &valid_doc.variables, + context, + ) + .await } /// Execute the reference introspection query in the provided schema