async-graphql/integrations/axum/src/extract.rs

149 lines
4.6 KiB
Rust
Raw Normal View History

use std::{io::ErrorKind, marker::PhantomData};
2021-08-01 09:44:28 +00:00
2022-04-19 04:25:11 +00:00
use async_graphql::{futures_util::TryStreamExt, http::MultipartOptions, ParseRequestError};
2021-08-21 21:43:20 +00:00
use axum::{
2022-10-12 14:42:07 +00:00
extract::{BodyStream, FromRequest},
2022-04-19 04:25:11 +00:00
http,
http::Method,
response::IntoResponse,
2022-04-19 04:25:11 +00:00
BoxError,
2021-08-21 21:43:20 +00:00
};
2022-10-12 14:42:07 +00:00
use axum::http::Request;
2021-08-21 21:43:20 +00:00
use bytes::Bytes;
2021-08-01 09:44:28 +00:00
use tokio_util::compat::TokioAsyncReadCompatExt;
/// Extractor for GraphQL request.
pub struct GraphQLRequest<R = rejection::GraphQLRejection>(
pub async_graphql::Request,
PhantomData<R>,
);
2021-08-01 09:44:28 +00:00
impl<R> GraphQLRequest<R> {
2021-08-01 09:44:28 +00:00
/// Unwraps the value to `async_graphql::Request`.
#[must_use]
pub fn into_inner(self) -> async_graphql::Request {
self.0
}
}
/// Rejection response types.
pub mod rejection {
use async_graphql::ParseRequestError;
2022-04-19 04:25:11 +00:00
use axum::{
body::{boxed, Body, BoxBody},
http,
http::StatusCode,
response::IntoResponse,
};
2021-08-01 09:44:28 +00:00
/// Rejection used for [`GraphQLRequest`](GraphQLRequest).
pub struct GraphQLRejection(pub ParseRequestError);
impl IntoResponse for GraphQLRejection {
2021-12-06 04:57:15 +00:00
fn into_response(self) -> http::Response<BoxBody> {
2021-09-10 02:23:16 +00:00
match self.0 {
ParseRequestError::PayloadTooLarge => http::Response::builder()
.status(StatusCode::PAYLOAD_TOO_LARGE)
2021-12-06 04:57:15 +00:00
.body(boxed(Body::empty()))
2021-09-10 02:23:16 +00:00
.unwrap(),
bad_request => http::Response::builder()
.status(StatusCode::BAD_REQUEST)
2021-12-06 04:57:15 +00:00
.body(boxed(Body::from(format!("{:?}", bad_request))))
2021-09-10 02:23:16 +00:00
.unwrap(),
}
2021-08-01 09:44:28 +00:00
}
}
impl From<ParseRequestError> for GraphQLRejection {
fn from(err: ParseRequestError) -> Self {
GraphQLRejection(err)
}
}
}
#[async_trait::async_trait]
2022-10-12 14:42:07 +00:00
impl<S, B, R> FromRequest<S, B> for GraphQLRequest<R>
2021-08-01 09:44:28 +00:00
where
2022-10-12 14:42:07 +00:00
S: Send + Sync,
2021-08-01 09:44:28 +00:00
B: http_body::Body + Unpin + Send + Sync + 'static,
2021-08-21 21:43:20 +00:00
B::Data: Into<Bytes>,
B::Error: Into<BoxError>,
R: IntoResponse + From<ParseRequestError>,
2021-08-01 09:44:28 +00:00
{
type Rejection = R;
2021-08-01 09:44:28 +00:00
2022-10-12 14:42:07 +00:00
async fn from_request(req: Request<B>, state: &S) -> Result<Self, Self::Rejection> {
2021-08-01 09:44:28 +00:00
Ok(GraphQLRequest(
2022-10-12 14:42:07 +00:00
GraphQLBatchRequest::<R>::from_request(req, state)
2021-08-01 09:44:28 +00:00
.await?
.0
.into_single()?,
PhantomData,
2021-08-01 09:44:28 +00:00
))
}
}
/// Extractor for GraphQL batch request.
pub struct GraphQLBatchRequest<R = rejection::GraphQLRejection>(
pub async_graphql::BatchRequest,
PhantomData<R>,
);
2021-08-01 09:44:28 +00:00
impl<R> GraphQLBatchRequest<R> {
2021-08-01 09:44:28 +00:00
/// Unwraps the value to `async_graphql::BatchRequest`.
#[must_use]
pub fn into_inner(self) -> async_graphql::BatchRequest {
self.0
}
}
#[async_trait::async_trait]
2022-10-12 14:42:07 +00:00
impl<S, B, R> FromRequest<S, B> for GraphQLBatchRequest<R>
2021-08-01 09:44:28 +00:00
where
2022-10-12 14:42:07 +00:00
S: Send + Sync,
2021-08-01 09:44:28 +00:00
B: http_body::Body + Unpin + Send + Sync + 'static,
2021-08-21 21:43:20 +00:00
B::Data: Into<Bytes>,
B::Error: Into<BoxError>,
R: IntoResponse + From<ParseRequestError>,
2021-08-01 09:44:28 +00:00
{
type Rejection = R;
2021-08-01 09:44:28 +00:00
2022-10-12 14:42:07 +00:00
async fn from_request(req: Request<B>, state: &S) -> Result<Self, Self::Rejection> {
2021-08-21 21:43:20 +00:00
if let (&Method::GET, uri) = (req.method(), req.uri()) {
2022-09-25 05:41:36 +00:00
let res = async_graphql::http::parse_query_string(uri.query().unwrap_or_default())
.map_err(|err| {
ParseRequestError::Io(std::io::Error::new(
ErrorKind::Other,
2022-10-12 14:42:07 +00:00
format!("failed to parse graphql request from uri query: {:?}", err),
2022-09-25 05:41:36 +00:00
))
});
Ok(Self(async_graphql::BatchRequest::Single(res?), PhantomData))
2021-08-01 09:44:28 +00:00
} else {
let content_type = req
.headers()
2022-04-04 05:02:55 +00:00
.get(http::header::CONTENT_TYPE)
2021-08-01 09:44:28 +00:00
.and_then(|value| value.to_str().ok())
.map(ToString::to_string);
2022-10-12 14:42:07 +00:00
let body_stream = BodyStream::from_request(req, state)
2021-08-01 09:44:28 +00:00
.await
.map_err(|_| {
ParseRequestError::Io(std::io::Error::new(
ErrorKind::Other,
"body has been taken by another extractor".to_string(),
))
})?
.map_err(|err| std::io::Error::new(ErrorKind::Other, err.to_string()));
let body_reader = tokio_util::io::StreamReader::new(body_stream).compat();
Ok(Self(
async_graphql::http::receive_batch_body(
content_type,
body_reader,
MultipartOptions::default(),
)
.await?,
PhantomData,
2021-08-01 09:44:28 +00:00
))
}
}
}