1
1
use std:: io:: { Cursor , Read } ;
2
+ use std:: error:: Error ;
2
3
3
4
use serde_json;
4
5
5
6
use rocket:: Request ;
6
- use rocket:: data:: { FromData , Outcome } ;
7
+ use rocket:: request:: { FromForm , FormItems , FromFormValue } ;
8
+ use rocket:: data:: { FromData , Outcome as FromDataOutcome } ;
7
9
use rocket:: response:: { Responder , Response , content} ;
8
10
use rocket:: http:: { ContentType , Status } ;
9
11
use rocket:: Data ;
10
12
use rocket:: Outcome :: { Forward , Failure , Success } ;
11
13
14
+ use :: InputValue ;
12
15
use :: http;
13
16
14
17
use types:: base:: GraphQLType ;
@@ -39,10 +42,62 @@ impl GraphQLRequest {
39
42
}
40
43
}
41
44
45
+ impl < ' f > FromForm < ' f > for GraphQLRequest {
46
+ type Error = String ;
47
+
48
+ fn from_form_items ( form_items : & mut FormItems < ' f > ) -> Result < Self , String > {
49
+ let mut query = None ;
50
+ let mut operation_name = None ;
51
+ let mut variables = None ;
52
+
53
+ for ( key, value) in form_items {
54
+ match key {
55
+ "query" => {
56
+ if query. is_some ( ) {
57
+ return Err ( "Query parameter must not occur more than once" . to_owned ( ) ) ;
58
+ }
59
+ else {
60
+ query = Some ( String :: from_form_value ( value) ?) ;
61
+ }
62
+ }
63
+ "operation_name" => {
64
+ if operation_name. is_some ( ) {
65
+ return Err ( "Operation name parameter must not occur more than once" . to_owned ( ) ) ;
66
+ }
67
+ else {
68
+ operation_name = Some ( String :: from_form_value ( value) ?) ;
69
+ }
70
+ }
71
+ "variables" => {
72
+ if variables. is_some ( ) {
73
+ return Err ( "Variables parameter must not occur more than once" . to_owned ( ) ) ;
74
+ }
75
+ else {
76
+ variables = Some ( serde_json:: from_str :: < InputValue > ( & String :: from_form_value ( value) ?)
77
+ . map_err ( |err| err. description ( ) . to_owned ( ) ) ?) ;
78
+ }
79
+ }
80
+ _ => { }
81
+ }
82
+ }
83
+
84
+ if let Some ( query) = query {
85
+ Ok ( GraphQLRequest ( http:: GraphQLRequest :: new (
86
+ query,
87
+ operation_name,
88
+ variables
89
+ ) ) )
90
+ }
91
+ else {
92
+ Err ( "Query parameter missing" . to_owned ( ) )
93
+ }
94
+ }
95
+ }
96
+
42
97
impl FromData for GraphQLRequest {
43
98
type Error = String ;
44
99
45
- fn from_data ( request : & Request , data : Data ) -> Outcome < Self , String > {
100
+ fn from_data ( request : & Request , data : Data ) -> FromDataOutcome < Self , String > {
46
101
if !request. content_type ( ) . map_or ( false , |ct| ct. is_json ( ) ) {
47
102
return Forward ( data) ;
48
103
}
@@ -72,3 +127,89 @@ impl<'r> Responder<'r> for GraphQLResponse {
72
127
. finalize ( ) )
73
128
}
74
129
}
130
+
131
+ #[ cfg( test) ]
132
+ mod tests {
133
+ use rocket;
134
+ use rocket:: Rocket ;
135
+ use rocket:: http:: { ContentType , Method } ;
136
+ use rocket:: State ;
137
+ use rocket:: testing:: MockRequest ;
138
+
139
+ use :: RootNode ;
140
+ use :: tests:: model:: Database ;
141
+ use :: http:: tests as http_tests;
142
+ use types:: scalars:: EmptyMutation ;
143
+
144
+ type Schema = RootNode < ' static , Database , EmptyMutation < Database > > ;
145
+
146
+ #[ get( "/?<request>" ) ]
147
+ fn get_graphql_handler (
148
+ context : State < Database > ,
149
+ request : super :: GraphQLRequest ,
150
+ schema : State < Schema > ,
151
+ ) -> super :: GraphQLResponse {
152
+ request. execute ( & schema, & context)
153
+ }
154
+
155
+ #[ post( "/" , data="<request>" ) ]
156
+ fn post_graphql_handler (
157
+ context : State < Database > ,
158
+ request : super :: GraphQLRequest ,
159
+ schema : State < Schema > ,
160
+ ) -> super :: GraphQLResponse {
161
+ request. execute ( & schema, & context)
162
+ }
163
+
164
+ struct TestRocketIntegration {
165
+ rocket : Rocket ,
166
+ }
167
+
168
+ impl http_tests:: HTTPIntegration for TestRocketIntegration
169
+ {
170
+ fn get ( & self , url : & str ) -> http_tests:: TestResponse {
171
+ make_test_response ( & self . rocket , MockRequest :: new (
172
+ Method :: Get ,
173
+ url) )
174
+ }
175
+
176
+ fn post ( & self , url : & str , body : & str ) -> http_tests:: TestResponse {
177
+ make_test_response (
178
+ & self . rocket ,
179
+ MockRequest :: new (
180
+ Method :: Post ,
181
+ url,
182
+ ) . header ( ContentType :: JSON ) . body ( body) )
183
+ }
184
+ }
185
+
186
+ #[ test]
187
+ fn test_rocket_integration ( ) {
188
+ let integration = TestRocketIntegration {
189
+ rocket : make_rocket ( ) ,
190
+ } ;
191
+
192
+ http_tests:: run_http_test_suite ( & integration) ;
193
+ }
194
+
195
+ fn make_rocket ( ) -> Rocket {
196
+ rocket:: ignite ( )
197
+ . manage ( Database :: new ( ) )
198
+ . manage ( Schema :: new ( Database :: new ( ) , EmptyMutation :: < Database > :: new ( ) ) )
199
+ . mount ( "/" , routes ! [ post_graphql_handler, get_graphql_handler] )
200
+ }
201
+
202
+ fn make_test_response < ' r > ( rocket : & ' r Rocket , mut request : MockRequest < ' r > ) -> http_tests:: TestResponse {
203
+ let mut response = request. dispatch_with ( & rocket) ;
204
+ let status_code = response. status ( ) . code as i32 ;
205
+ let content_type = response. header_values ( "content-type" ) . collect :: < Vec < _ > > ( ) . into_iter ( ) . next ( )
206
+ . expect ( "No content type header from handler" ) . to_owned ( ) ;
207
+ let body = response. body ( ) . expect ( "No body returned from GraphQL handler" ) . into_string ( ) ;
208
+
209
+ http_tests:: TestResponse {
210
+ status_code : status_code,
211
+ body : body,
212
+ content_type : content_type,
213
+ }
214
+ }
215
+ }
0 commit comments