Add support for parse request from query string #1085
This commit is contained in:
parent
73f6b0b136
commit
053c1178c2
|
@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file.
|
|||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
# [4.0.14] 2022-09-25
|
||||
|
||||
# Add support for parse request from query string. [#1085](https://github.com/async-graphql/async-graphql/issues/1085)
|
||||
|
||||
# [4.0.13] 2022-09-09
|
||||
|
||||
- Compare to expected schema [#1048](https://github.com/async-graphql/async-graphql/pull/1048)
|
||||
|
|
|
@ -54,6 +54,7 @@ static_assertions = "1.1.0"
|
|||
tempfile = "3.2.0"
|
||||
thiserror = "1.0.24"
|
||||
base64 = "0.13.0"
|
||||
serde_urlencoded = "0.7.0"
|
||||
|
||||
# Feature optional dependencies
|
||||
bson = { version = "2.4.0", optional = true, features = [
|
||||
|
|
|
@ -23,7 +23,6 @@ futures-channel = "0.3.13"
|
|||
futures-util = { version = "0.3.0", default-features = false }
|
||||
serde_cbor = { version = "0.11.2", optional = true }
|
||||
serde_json = "1.0.64"
|
||||
serde_urlencoded = "0.7.0"
|
||||
thiserror = "1.0.30"
|
||||
|
||||
[features]
|
||||
|
|
|
@ -75,7 +75,8 @@ impl FromRequest for GraphQLBatchRequest {
|
|||
.unwrap_or_default();
|
||||
|
||||
if req.method() == Method::GET {
|
||||
let res = serde_urlencoded::from_str(req.query_string());
|
||||
let res = async_graphql::http::parse_query_string(req.query_string())
|
||||
.map_err(|err| io::Error::new(ErrorKind::Other, err));
|
||||
Box::pin(async move { Ok(Self(async_graphql::BatchRequest::Single(res?))) })
|
||||
} else if req.method() == Method::POST {
|
||||
let content_type = req
|
||||
|
|
|
@ -20,6 +20,5 @@ bytes = "1.0.1"
|
|||
futures-util = "0.3.0"
|
||||
http-body = "0.4.2"
|
||||
serde_json = "1.0.66"
|
||||
serde_urlencoded = "0.7.0"
|
||||
tokio-util = { version = "0.7.1", features = ["io", "compat"] }
|
||||
tower-service = "0.3"
|
||||
|
|
|
@ -107,7 +107,7 @@ where
|
|||
|
||||
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
|
||||
if let (&Method::GET, uri) = (req.method(), req.uri()) {
|
||||
let res = serde_urlencoded::from_str(uri.query().unwrap_or_default()).map_err(|err| {
|
||||
let res = async_graphql::http::parse_query_string(uri.query().unwrap_or_default()).map_err(|err| {
|
||||
ParseRequestError::Io(std::io::Error::new(
|
||||
ErrorKind::Other,
|
||||
format!("failed to parse graphql request from uri query: {}", err),
|
||||
|
|
|
@ -3,7 +3,6 @@ use poem::{
|
|||
async_trait,
|
||||
error::BadRequest,
|
||||
http::{header, Method},
|
||||
web::Query,
|
||||
FromRequest, Request, RequestBody, Result,
|
||||
};
|
||||
use tokio_util::compat::TokioAsyncReadCompatExt;
|
||||
|
@ -68,7 +67,9 @@ pub struct GraphQLBatchRequest(pub async_graphql::BatchRequest);
|
|||
impl<'a> FromRequest<'a> for GraphQLBatchRequest {
|
||||
async fn from_request(req: &'a Request, body: &mut RequestBody) -> Result<Self> {
|
||||
if req.method() == Method::GET {
|
||||
let req = Query::from_request(req, body).await?.0;
|
||||
let req =
|
||||
async_graphql::http::parse_query_string(req.uri().query().unwrap_or_default())
|
||||
.map_err(BadRequest)?;
|
||||
Ok(Self(async_graphql::BatchRequest::Single(req)))
|
||||
} else {
|
||||
let content_type = req
|
||||
|
|
|
@ -132,7 +132,9 @@ pub async fn receive_batch_request_opts<State: Clone + Send + Sync + 'static>(
|
|||
opts: MultipartOptions,
|
||||
) -> tide::Result<async_graphql::BatchRequest> {
|
||||
if request.method() == Method::Get {
|
||||
request.query::<async_graphql::Request>().map(Into::into)
|
||||
async_graphql::http::parse_query_string(request.url().query().unwrap_or_default())
|
||||
.map(Into::into)
|
||||
.map_err(|err| tide::Error::new(StatusCode::BadRequest, err))
|
||||
} else if request.method() == Method::Post {
|
||||
let body = request.take_body();
|
||||
let content_type = request
|
||||
|
|
|
@ -35,7 +35,13 @@ where
|
|||
Subscription: SubscriptionType + 'static,
|
||||
{
|
||||
warp::any()
|
||||
.and(warp::get().and(warp::query()).map(BatchRequest::Single))
|
||||
.and(warp::get().and(warp::filters::query::raw()).and_then(
|
||||
|query_string: String| async move {
|
||||
async_graphql::http::parse_query_string(&query_string)
|
||||
.map(Into::into)
|
||||
.map_err(|e| warp::reject::custom(GraphQLBadRequest(e)))
|
||||
},
|
||||
))
|
||||
.or(warp::post()
|
||||
.and(warp::header::optional::<String>("content-type"))
|
||||
.and(warp::body::stream())
|
||||
|
|
|
@ -6,18 +6,59 @@ mod multipart;
|
|||
mod playground_source;
|
||||
mod websocket;
|
||||
|
||||
use std::io::ErrorKind;
|
||||
|
||||
use futures_util::io::{AsyncRead, AsyncReadExt};
|
||||
pub use graphiql_source::graphiql_source;
|
||||
pub use graphiql_v2_source::GraphiQLSource;
|
||||
use mime;
|
||||
pub use multipart::MultipartOptions;
|
||||
pub use playground_source::{playground_source, GraphQLPlaygroundConfig};
|
||||
use serde::Deserialize;
|
||||
pub use websocket::{
|
||||
ClientMessage, Protocols as WebSocketProtocols, WebSocket, WsMessage, ALL_WEBSOCKET_PROTOCOLS,
|
||||
};
|
||||
|
||||
use crate::{BatchRequest, ParseRequestError, Request};
|
||||
|
||||
/// Parse a GraphQL request from a query string.
|
||||
pub fn parse_query_string(input: &str) -> Result<Request, ParseRequestError> {
|
||||
#[derive(Deserialize)]
|
||||
struct RequestSerde {
|
||||
#[serde(default)]
|
||||
pub query: String,
|
||||
pub operation_name: Option<String>,
|
||||
pub variables: Option<String>,
|
||||
pub extensions: Option<String>,
|
||||
}
|
||||
|
||||
let request: RequestSerde = serde_urlencoded::from_str(input)
|
||||
.map_err(|err| std::io::Error::new(ErrorKind::Other, err))?;
|
||||
let variables = request
|
||||
.variables
|
||||
.map(|data| serde_json::from_str(&data))
|
||||
.transpose()
|
||||
.map_err(|err| {
|
||||
std::io::Error::new(ErrorKind::Other, format!("invalid variables: {}", err))
|
||||
})?
|
||||
.unwrap_or_default();
|
||||
let extensions = request
|
||||
.extensions
|
||||
.map(|data| serde_json::from_str(&data))
|
||||
.transpose()
|
||||
.map_err(|err| {
|
||||
std::io::Error::new(ErrorKind::Other, format!("invalid extensions: {}", err))
|
||||
})?
|
||||
.unwrap_or_default();
|
||||
|
||||
Ok(Request {
|
||||
operation_name: request.operation_name,
|
||||
variables,
|
||||
extensions,
|
||||
..Request::new(request.query)
|
||||
})
|
||||
}
|
||||
|
||||
/// Receive a GraphQL request from a content type and body.
|
||||
pub async fn receive_body(
|
||||
content_type: Option<impl AsRef<str>>,
|
||||
|
@ -116,3 +157,33 @@ pub async fn receive_batch_cbor(body: impl AsyncRead) -> Result<BatchRequest, Pa
|
|||
serde_cbor::from_slice::<BatchRequest>(&data)
|
||||
.map_err(|e| ParseRequestError::InvalidRequest(Box::new(e)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::collections::HashMap;
|
||||
|
||||
use super::*;
|
||||
use crate::{value, Variables};
|
||||
|
||||
#[test]
|
||||
fn test_parse_query_string() {
|
||||
let request = parse_query_string("variables=%7B%7D&extensions=%7B%22persistedQuery%22%3A%7B%22sha256Hash%22%3A%22cde5de0a350a19c59f8ddcd9646e5f260b2a7d5649ff6be8e63e9462934542c3%22%2C%22version%22%3A1%7D%7D").unwrap();
|
||||
assert_eq!(request.query.as_str(), "");
|
||||
assert_eq!(request.variables, Variables::default());
|
||||
assert_eq!(request.extensions, {
|
||||
let mut extensions = HashMap::new();
|
||||
extensions.insert("persistedQuery".to_string(), value!({
|
||||
"sha256Hash": "cde5de0a350a19c59f8ddcd9646e5f260b2a7d5649ff6be8e63e9462934542c3",
|
||||
"version": 1,
|
||||
}));
|
||||
extensions
|
||||
});
|
||||
|
||||
let request = parse_query_string("query={a}&variables=%7B%22a%22%3A10%7D").unwrap();
|
||||
assert_eq!(request.query.as_str(), "{a}");
|
||||
assert_eq!(
|
||||
request.variables,
|
||||
Variables::from_value(value!({ "a" : 10 }))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ use serde::{Deserialize, Deserializer, Serialize};
|
|||
use crate::{ConstValue, Name};
|
||||
|
||||
/// Variables of a query.
|
||||
#[derive(Debug, Clone, Default, Serialize)]
|
||||
#[derive(Debug, Clone, Default, Serialize, Eq, PartialEq)]
|
||||
#[serde(transparent)]
|
||||
pub struct Variables(BTreeMap<Name, ConstValue>);
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user