diff --git a/juniper/src/http/mod.rs b/juniper/src/http/mod.rs index c448da2d..79e3d8d8 100644 --- a/juniper/src/http/mod.rs +++ b/juniper/src/http/mod.rs @@ -4,6 +4,7 @@ pub mod graphiql; pub mod playground; use serde::{ + de, ser::{self, SerializeMap}, Deserialize, Serialize, }; @@ -216,7 +217,7 @@ where } } -/// Simple wrapper around GraphQLRequest to allow the handling of Batch requests +/// Simple wrapper around GraphQLRequest to allow the handling of Batch requests. #[derive(Debug, Deserialize, PartialEq)] #[serde(untagged)] #[serde(bound = "InputValue: Deserialize<'de>")] @@ -226,10 +227,29 @@ where { /// A single operation request. Single(GraphQLRequest), + /// A batch operation request. + /// + /// Empty batch is considered as invalid value, so cannot be deserialized. + #[serde(deserialize_with = "deserialize_non_empty_vec")] Batch(Vec>), } +fn deserialize_non_empty_vec<'de, D, T>(deserializer: D) -> Result, D::Error> +where + D: de::Deserializer<'de>, + T: Deserialize<'de>, +{ + use de::Error as _; + + let v = Vec::::deserialize(deserializer)?; + if v.is_empty() { + Err(D::Error::invalid_length(0, &"a positive integer")) + } else { + Ok(v) + } +} + impl GraphQLBatchRequest where S: ScalarValue, @@ -373,6 +393,9 @@ pub mod tests { println!(" - test_batched_post"); test_batched_post(integration); + println!(" - test_empty_batched_post"); + test_empty_batched_post(integration); + println!(" - test_invalid_json"); test_invalid_json(integration); @@ -499,6 +522,11 @@ pub mod tests { ); } + fn test_empty_batched_post(integration: &T) { + let response = integration.post("/", "[]"); + assert_eq!(response.status_code, 400); + } + fn test_invalid_json(integration: &T) { let response = integration.get("/?query=blah"); assert_eq!(response.status_code, 400);