Skip to content

Commit d3b1433

Browse files
Added more support for get requests and the get and post handlers leverage the GraphQlQuery struct. Added a couple more tests for get requests.
1 parent 998faec commit d3b1433

File tree

1 file changed

+185
-101
lines changed

1 file changed

+185
-101
lines changed

src/integrations/iron_handlers.rs

Lines changed: 185 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,23 @@
11
//! Optional handlers for the Iron framework. Requires the `iron-handlers` feature enabled.
2-
32
use iron::prelude::*;
43
use iron::middleware::Handler;
54
use iron::mime::Mime;
65
use iron::status;
76
use iron::method;
8-
use iron::url::Url;
7+
use urlencoded::{UrlEncodedQuery, UrlDecodingError};
98

109
use std::io::Read;
1110
use std::io::Error as IoError;
1211
use std::io::ErrorKind;
13-
use std::collections::HashMap;
1412
use std::error::Error;
1513
use std::fmt;
1614
use std::boxed::Box;
1715

1816
use serde_json;
19-
use serde_json::Value as Json;
2017
use serde_json::error::Error as SerdeError;
2118

22-
use ::{InputValue, GraphQLType, RootNode, Variables, execute as execute_query};
19+
use ::{InputValue, GraphQLType, RootNode, execute};
20+
use super::serde::{WrappedGraphQLResult, GraphQlQuery};
2321

2422
/// Handler that executes GraphQL queries in the given schema
2523
///
@@ -46,6 +44,53 @@ pub struct GraphiQLHandler {
4644
graphql_url: String,
4745
}
4846

47+
48+
/// Get queries are allowed to repeat the same key more than once.
49+
fn check_for_repeat_keys(params: &Vec<String>) -> Result<(), IronError> {
50+
if params.len() > 1 {
51+
let error = IronError::new(
52+
Box::new(GraphQlIronError::IO(IoError::new(ErrorKind::InvalidData,
53+
"Was able parse a query string \
54+
but a duplicate uri key was \
55+
found."))),
56+
(status::BadRequest, "Duplicate uri key was found."));
57+
Err(error)
58+
}
59+
else {
60+
Ok(())
61+
}
62+
}
63+
64+
fn parse_url_param(param: Option<Vec<String>>) -> Result<Option<String>, IronError> {
65+
if let Some(values) = param {
66+
check_for_repeat_keys(&values)?;
67+
Ok(Some(values[0].to_owned()))
68+
}
69+
else {
70+
Ok(None)
71+
}
72+
}
73+
74+
fn parse_variable_param(param: Option<Vec<String>>) -> Result<Option<InputValue>, IronError> {
75+
if let Some(values) = param {
76+
check_for_repeat_keys(&values)?;
77+
match serde_json::from_str::<InputValue>(values[0].as_ref()) {
78+
Ok(input_values) => {
79+
Ok(Some(input_values))
80+
}
81+
Err(err) => {
82+
Err(IronError::new(
83+
Box::new(GraphQlIronError::Serde(err)),
84+
(status::BadRequest, "No JSON object was decoded.")))
85+
}
86+
}
87+
}
88+
else {
89+
Ok(None)
90+
}
91+
}
92+
93+
4994
impl<'a, CtxFactory, Query, Mutation, CtxT>
5095
GraphQLHandler<'a, CtxFactory, Query, Mutation, CtxT>
5196
where CtxFactory: Fn(&mut Request) -> CtxT + Send + Sync + 'static,
@@ -67,96 +112,62 @@ impl<'a, CtxFactory, Query, Mutation, CtxT>
67112
}
68113

69114

70-
fn handle_get(&self, req: &mut Request) -> IronResult<Response> {
71-
let url: Url = req.url.clone().into();
72-
73-
let mut query = None;
74-
let variables = Variables::new();
75-
76-
for (k, v) in url.query_pairs() {
77-
if k == "query" {
78-
query = Some(v.into_owned());
115+
fn handle_get(&self, req: &mut Request) -> IronResult<GraphQlQuery> {
116+
match req.get_mut::<UrlEncodedQuery>() {
117+
Ok(ref mut query_string) => {
118+
let input_query = parse_url_param(query_string.remove("query").to_owned())?;
119+
if let Some(query) = input_query {
120+
let operation_name =
121+
parse_url_param(query_string.remove("operationName"))?;
122+
let input_variables =
123+
parse_variable_param(query_string.remove("variables"))?;
124+
Ok(GraphQlQuery::new(query,operation_name,input_variables))
125+
} else {
126+
Err(IronError::new(
127+
Box::new(GraphQlIronError::IO(IoError::new(ErrorKind::InvalidData,
128+
"No query key was found in \
129+
the Get request."))),
130+
(status::BadRequest, "No query was provided.")))
131+
}
132+
}
133+
Err(err) => {
134+
Err(IronError::new(
135+
Box::new(GraphQlIronError::Url(err)),
136+
(status::BadRequest, "No JSON object was decoded.")))
79137
}
80138
}
81-
82-
let query = iexpect!(query);
83-
84-
self.execute(req, &query, &variables)
85139
}
86140

87-
fn handle_post(&self, req: &mut Request) -> IronResult<Response> {
141+
fn handle_post(&self, req: &mut Request) -> IronResult<GraphQlQuery> {
88142
let mut request_payload = String::new();
89143
itry!(req.body.read_to_string(&mut request_payload));
90-
let json_data =
91-
match serde_json::from_str::<Json>(&*request_payload) {
92-
Ok(json) => json,
93-
Err(err) => {
94-
let error = IronError::new(
95-
Box::new(GraphQlIronError::Serde(err)),
96-
(status::BadRequest, "No JSON object was decoded."));
97-
return Err(error)
98-
}
99-
};
100-
match json_data {
101-
Json::Object(json_obj) => {
102-
let mut query = None;
103-
let mut variables = Variables::new();
104-
for (k, v) in json_obj {
105-
if k == "query" {
106-
query = v.as_str().map(|query| query.to_owned());
107-
}
108-
else if k == "variables" {
109-
variables = InputValue::from_json(v).to_object_value()
110-
.map(|o| o.into_iter().map(|(k, v)| (k.to_owned(), v.clone())).collect())
111-
.unwrap_or_default();
112-
}
113-
}
114-
let query = iexpect!(query);
115-
self.execute(req, &query, &variables)
116-
}
117-
_ => {
118-
let error = IronError::new(
119-
Box::new(GraphQlIronError::IO(IoError::new(ErrorKind::InvalidData,
120-
"Was able parse a JSON item but it\
121-
was not an object as expected."))),
122-
(status::BadRequest, "No JSON object was decoded."));
123-
Err(error)
124-
}
125-
}
144+
let graphql_query = serde_json::from_str::<GraphQlQuery>(request_payload.as_str()).map_err(|err|{
145+
IronError::new(
146+
Box::new(GraphQlIronError::Serde(err)),
147+
(status::BadRequest, "No JSON object was decoded."))
148+
});
149+
graphql_query
126150
}
127151

128-
fn execute(&self, req: &mut Request, query: &str, variables: &Variables) -> IronResult<Response> {
129-
let context = (self.context_factory)(req);
130-
let result = execute_query(query, None, &self.root_node, variables, &context);
131-
let content_type = "application/json".parse::<Mime>().unwrap();
132-
let mut map = HashMap::new();
133-
134-
match result {
135-
Ok((result, errors)) => {
136-
let response_data = serde_json::to_value(result)
137-
.expect("Failed to convert response data to JSON.");
138-
map.insert("data".to_owned(), response_data);
139-
140-
if !errors.is_empty() {
141-
let response_data = serde_json::to_value(errors)
142-
.expect("Failed to convert the errors to JSON.");
143-
map.insert("errors".to_owned(), response_data);
144-
}
145-
let data = serde_json::to_value(map).expect("Failed to convert response to JSON");
146-
let json = serde_json::to_string_pretty(&data).expect("Failed to convert response to JSON.");
147-
Ok(Response::with((content_type, status::Ok, json)))
148-
}
149-
Err(err) => {
150-
let response_data = serde_json::to_value(err)
151-
.expect("Failed to convert error data to JSON.");
152-
map.insert("errors".to_owned(), response_data);
153-
let data = serde_json::to_value(map).expect("Failed to convert response to JSON");
154-
let json = serde_json::to_string_pretty(&data)
155-
.expect("Failed to convert response to JSON");
156-
Ok(Response::with((content_type, status::BadRequest, json.to_string())))
157-
}
158-
}
159-
}
152+
fn respond(&self, req: &mut Request, graphql: GraphQlQuery) -> IronResult<Response> {
153+
let context = (self.context_factory)(req);
154+
let variables = graphql.variables();
155+
let result = execute(graphql.query(),
156+
graphql.operation_name(),
157+
&self.root_node,
158+
&variables,
159+
&context);
160+
let content_type = "application/json".parse::<Mime>().unwrap();
161+
if result.is_ok() {
162+
let response = WrappedGraphQLResult::new(result);
163+
let json = serde_json::to_string_pretty(&response).unwrap();
164+
Ok(Response::with((content_type, status::Ok, json)))
165+
} else {
166+
let response = WrappedGraphQLResult::new(result);
167+
let json = serde_json::to_string_pretty(&response).unwrap();
168+
Ok(Response::with((content_type, status::BadRequest, json)))
169+
}
170+
}
160171
}
161172

162173
impl GraphiQLHandler {
@@ -179,10 +190,16 @@ impl<'a, CtxFactory, Query, Mutation, CtxT>
179190
Query: GraphQLType<Context=CtxT> + Send + Sync + 'static,
180191
Mutation: GraphQLType<Context=CtxT> + Send + Sync + 'static, 'a: 'static,
181192
{
182-
fn handle(&self, req: &mut Request) -> IronResult<Response> {
193+
fn handle(&self, mut req: &mut Request) -> IronResult<Response> {
183194
match req.method {
184-
method::Get => self.handle_get(req),
185-
method::Post => self.handle_post(req),
195+
method::Get => {
196+
let graphql_query = self.handle_get(&mut req)?;
197+
self.respond(&mut req, graphql_query)
198+
}
199+
method::Post => {
200+
let graphql_query = self.handle_post(&mut req)?;
201+
self.respond(&mut req, graphql_query)
202+
},
186203
_ => Ok(Response::with((status::MethodNotAllowed)))
187204
}
188205
}
@@ -202,7 +219,6 @@ impl Handler for GraphiQLHandler {
202219
}
203220
</style>
204221
"#;
205-
206222
let fetcher_source = r#"
207223
<script>
208224
function graphQLFetcher(params) {
@@ -260,27 +276,22 @@ impl Handler for GraphiQLHandler {
260276
}
261277

262278
/// A general error allowing the developer to see the underlying issue.
279+
#[derive(Debug)]
263280
pub enum GraphQlIronError {
264281
///Captures any errors that were caused by Serde.
265282
Serde(SerdeError),
266283
/// Captures any error related the IO.
267-
IO(IoError)
284+
IO(IoError),
285+
/// Captures any error related to Url Decoding,
286+
Url(UrlDecodingError)
268287
}
269288

270289
impl fmt::Display for GraphQlIronError {
271290
fn fmt(&self, mut f: &mut fmt::Formatter) -> fmt::Result {
272291
match *self {
273292
GraphQlIronError::Serde(ref err) => fmt::Display::fmt(err, &mut f),
274293
GraphQlIronError::IO(ref err) => fmt::Display::fmt(err, &mut f),
275-
}
276-
}
277-
}
278-
279-
impl fmt::Debug for GraphQlIronError {
280-
fn fmt(&self, mut f: &mut fmt::Formatter) -> fmt::Result {
281-
match *self {
282-
GraphQlIronError::Serde(ref err) => fmt::Debug::fmt(err, &mut f),
283-
GraphQlIronError::IO(ref err) => fmt::Debug::fmt(err, &mut f),
294+
GraphQlIronError::Url(ref err) => fmt::Display::fmt(err, &mut f),
284295
}
285296
}
286297
}
@@ -294,6 +305,9 @@ impl Error for GraphQlIronError {
294305
GraphQlIronError::IO(ref err) => {
295306
err.description()
296307
}
308+
GraphQlIronError::Url(ref err) => {
309+
err.description()
310+
}
297311
}
298312
}
299313

@@ -305,6 +319,9 @@ impl Error for GraphQlIronError {
305319
GraphQlIronError::IO(ref err) => {
306320
err.cause()
307321
}
322+
GraphQlIronError::Url(ref err) => {
323+
err.cause()
324+
}
308325
}
309326
}
310327
}
@@ -363,6 +380,73 @@ mod tests {
363380
.expect("Invalid JSON constant in test"));
364381
}
365382

383+
#[test]
384+
fn test_encoded_get() {
385+
let response = request::get(
386+
"http://localhost:3000/?query=query%20{%20%20%20human(id:%20\"1000\")%20{%20%20%20%20%20id,%20%20%20%20%20name,%20%20%20%20%20appearsIn,%20%20%20%20%20homePlanet%20%20%20}%20}",
387+
Headers::new(),
388+
&make_handler())
389+
.expect("Unexpected IronError");
390+
391+
assert_eq!(response.status, Some(status::Ok));
392+
assert_eq!(response.headers.get::<headers::ContentType>(),
393+
Some(&headers::ContentType::json()));
394+
395+
let json = unwrap_json_response(response);
396+
397+
assert_eq!(
398+
json,
399+
serde_json::from_str::<Json>(r#"{
400+
"data": {
401+
"human": {
402+
"appearsIn": [
403+
"NEW_HOPE",
404+
"EMPIRE",
405+
"JEDI"
406+
],
407+
"homePlanet": "Tatooine",
408+
"name": "Luke Skywalker",
409+
"id": "1000"
410+
}
411+
}
412+
}"#)
413+
.expect("Invalid JSON constant in test"));
414+
}
415+
416+
#[test]
417+
fn test_get_with_variables() {
418+
let response = request::get(
419+
"http://localhost:3000/?query=query($id:%20String!)%20{%20%20%20human(id:%20$id)%20{%20%20%20%20%20id,%20%20%20%20%20name,%20%20%20%20%20appearsIn,%20%20%20%20%20homePlanet%20%20%20}%20}&variables={%20%20%20\"id\":%20%20\"1000\"%20}",
420+
Headers::new(),
421+
&make_handler())
422+
.expect("Unexpected IronError");
423+
424+
assert_eq!(response.status, Some(status::Ok));
425+
assert_eq!(response.headers.get::<headers::ContentType>(),
426+
Some(&headers::ContentType::json()));
427+
428+
let json = unwrap_json_response(response);
429+
430+
assert_eq!(
431+
json,
432+
serde_json::from_str::<Json>(r#"{
433+
"data": {
434+
"human": {
435+
"appearsIn": [
436+
"NEW_HOPE",
437+
"EMPIRE",
438+
"JEDI"
439+
],
440+
"homePlanet": "Tatooine",
441+
"name": "Luke Skywalker",
442+
"id": "1000"
443+
}
444+
}
445+
}"#)
446+
.expect("Invalid JSON constant in test"));
447+
}
448+
449+
366450
#[test]
367451
fn test_simple_post() {
368452
let response = request::post(

0 commit comments

Comments
 (0)