diff --git a/src/http/websocket.rs b/src/http/websocket.rs index e8ad9ffd..1448a96a 100644 --- a/src/http/websocket.rs +++ b/src/http/websocket.rs @@ -365,6 +365,7 @@ impl std::str::FromStr for Protocols { /// A websocket message received from the client #[derive(Deserialize)] #[serde(tag = "type", rename_all = "snake_case")] +#[allow(clippy::large_enum_variant)] //Request is at fault pub enum ClientMessage { /// A new connection ConnectionInit { diff --git a/src/request.rs b/src/request.rs index 9132e7af..7e24d82f 100644 --- a/src/request.rs +++ b/src/request.rs @@ -4,7 +4,9 @@ use std::fmt::{self, Debug, Formatter}; use serde::{Deserialize, Deserializer, Serialize}; -use crate::{Data, ParseRequestError, UploadValue, Value, Variables}; +use crate::parser::parse_query; +use crate::parser::types::ExecutableDocument; +use crate::{Data, ParseRequestError, ServerError, UploadValue, Value, Variables}; /// GraphQL request. /// @@ -42,6 +44,9 @@ pub struct Request { /// Disable introspection queries for this request. #[serde(skip)] pub disable_introspection: bool, + + #[serde(skip)] + pub(crate) parsed_query: Option, } impl Request { @@ -55,6 +60,7 @@ impl Request { data: Data::default(), extensions: Default::default(), disable_introspection: false, + parsed_query: None, } } @@ -87,6 +93,24 @@ impl Request { self } + #[inline] + /// Performs parsing of query ahead of execution. + /// + /// This effectively allows to inspect query information, before passing request to schema for + /// execution as long as query is valid. + pub fn parsed_query(&mut self) -> Result<&ExecutableDocument, ServerError> { + if self.parsed_query.is_none() { + match parse_query(&self.query) { + Ok(parsed) => self.parsed_query = Some(parsed), + Err(error) => return Err(error.into()), + } + } + + //forbid_unsafe effectively bans optimize away else branch here so use unwrap + //but this unwrap never panics + Ok(self.parsed_query.as_ref().unwrap()) + } + /// Set a variable to an upload value. /// /// `var_path` is a dot-separated path to the item that begins with `variables`, for example @@ -141,6 +165,7 @@ impl Debug for Request { /// **Reference:** #[derive(Debug, Deserialize)] #[serde(untagged)] +#[allow(clippy::large_enum_variant)] //Request is at fault pub enum BatchRequest { /// Single query Single(Request), diff --git a/src/schema.rs b/src/schema.rs index bfdd0920..9480cfd9 100644 --- a/src/schema.rs +++ b/src/schema.rs @@ -427,13 +427,19 @@ where let query_data = Arc::new(std::mem::take(&mut request.data)); extensions.attach_query_data(query_data.clone()); - let request = extensions.prepare_request(request).await?; + let mut request = extensions.prepare_request(request).await?; let mut document = { let query = &request.query; - let fut_parse = async { parse_query(&query).map_err(Into::::into) }; + let parsed_doc = request.parsed_query.take(); + let fut_parse = async move { + match parsed_doc { + Some(parsed_doc) => Ok(parsed_doc), + None => parse_query(query).map_err(Into::into), + } + }; futures_util::pin_mut!(fut_parse); extensions - .parse_query(&query, &request.variables, &mut fut_parse) + .parse_query(query, &request.variables, &mut fut_parse) .await? };