gqlrs_rocket/
rocket_impl.rs1use core::any::Any;
2use std::io::Cursor;
3
4use async_graphql::{Executor, ParseRequestError, http::MultipartOptions};
5use rocket::{
6 data::{self, Data, FromData, ToByteUnit},
7 form::FromForm,
8 http::{ContentType, Header, Status},
9 response::{self, Responder},
10};
11use tokio_util::compat::TokioAsyncReadCompatExt;
12
13#[derive(Debug)]
24pub struct GraphQLBatchRequest(pub async_graphql::BatchRequest);
25
26impl GraphQLBatchRequest {
27 pub async fn execute<E>(self, executor: &E) -> GraphQLResponse
29 where
30 E: Executor,
31 {
32 GraphQLResponse(executor.execute_batch(self.0).await)
33 }
34}
35
36#[rocket::async_trait]
37impl<'r> FromData<'r> for GraphQLBatchRequest {
38 type Error = ParseRequestError;
39
40 async fn from_data(req: &'r rocket::Request<'_>, data: Data<'r>) -> data::Outcome<'r, Self> {
41 let opts: MultipartOptions = req.rocket().state().copied().unwrap_or_default();
42
43 let request = async_graphql::http::receive_batch_body(
44 req.headers().get_one("Content-Type"),
45 data.open(
46 req.limits()
47 .get("graphql")
48 .unwrap_or_else(|| 128.kibibytes()),
49 )
50 .compat(),
51 opts,
52 )
53 .await;
54
55 match request {
56 Ok(request) => data::Outcome::Success(Self(request)),
57 Err(e) => data::Outcome::Error((
58 match e {
59 ParseRequestError::PayloadTooLarge => Status::PayloadTooLarge,
60 _ => Status::BadRequest,
61 },
62 e,
63 )),
64 }
65 }
66}
67
68#[derive(Debug)]
79pub struct GraphQLRequest(pub async_graphql::Request);
80
81impl GraphQLRequest {
82 pub async fn execute<E>(self, executor: &E) -> GraphQLResponse
84 where
85 E: Executor,
86 {
87 GraphQLResponse(executor.execute(self.0).await.into())
88 }
89
90 #[must_use]
92 pub fn data<D: Any + Send + Sync>(mut self, data: D) -> Self {
93 self.0.data.insert(data);
94 self
95 }
96}
97
98impl From<GraphQLQuery> for GraphQLRequest {
99 fn from(query: GraphQLQuery) -> Self {
100 let mut request = async_graphql::Request::new(query.query);
101
102 if let Some(operation_name) = query.operation_name {
103 request = request.operation_name(operation_name);
104 }
105
106 if let Some(variables) = query.variables {
107 let value = serde_json::from_str(&variables).unwrap_or_default();
108 let variables = async_graphql::Variables::from_json(value);
109 request = request.variables(variables);
110 }
111
112 GraphQLRequest(request)
113 }
114}
115
116#[derive(FromForm, Debug)]
127pub struct GraphQLQuery {
128 query: String,
129 #[field(name = "operationName")]
130 operation_name: Option<String>,
131 variables: Option<String>,
132}
133
134impl GraphQLQuery {
135 pub async fn execute<E>(self, executor: &E) -> GraphQLResponse
137 where
138 E: Executor,
139 {
140 let request: GraphQLRequest = self.into();
141 request.execute(executor).await
142 }
143}
144
145#[rocket::async_trait]
146impl<'r> FromData<'r> for GraphQLRequest {
147 type Error = ParseRequestError;
148
149 async fn from_data(req: &'r rocket::Request<'_>, data: Data<'r>) -> data::Outcome<'r, Self> {
150 GraphQLBatchRequest::from_data(req, data)
151 .await
152 .and_then(|request| match request.0.into_single() {
153 Ok(single) => data::Outcome::Success(Self(single)),
154 Err(e) => data::Outcome::Error((Status::BadRequest, e)),
155 })
156 }
157}
158
159#[derive(Debug)]
165pub struct GraphQLResponse(pub async_graphql::BatchResponse);
166
167impl From<async_graphql::BatchResponse> for GraphQLResponse {
168 fn from(batch: async_graphql::BatchResponse) -> Self {
169 Self(batch)
170 }
171}
172impl From<async_graphql::Response> for GraphQLResponse {
173 fn from(res: async_graphql::Response) -> Self {
174 Self(res.into())
175 }
176}
177
178impl<'r> Responder<'r, 'static> for GraphQLResponse {
179 fn respond_to(self, _: &'r rocket::Request<'_>) -> response::Result<'static> {
180 let body = serde_json::to_string(&self.0).unwrap();
181
182 let mut response = rocket::Response::new();
183 response.set_header(ContentType::new("application", "json"));
184
185 if self.0.is_ok()
186 && let Some(cache_control) = self.0.cache_control().value()
187 {
188 response.set_header(Header::new("cache-control", cache_control));
189 }
190
191 for (name, value) in self.0.http_headers_iter() {
192 if let Ok(value) = value.to_str() {
193 response.adjoin_header(Header::new(name.as_str().to_string(), value.to_string()));
194 }
195 }
196
197 response.set_sized_body(body.len(), Cursor::new(body));
198
199 Ok(response)
200 }
201}