@@ -3,8 +3,10 @@ package handler
3
3
import (
4
4
"encoding/json"
5
5
"io/ioutil"
6
+ "mime/multipart"
6
7
"net/http"
7
8
"net/url"
9
+ "strconv"
8
10
"strings"
9
11
10
12
"github.com/graphql-go/graphql"
@@ -13,17 +15,24 @@ import (
13
15
)
14
16
15
17
const (
16
- ContentTypeJSON = "application/json"
17
- ContentTypeGraphQL = "application/graphql"
18
- ContentTypeFormURLEncoded = "application/x-www-form-urlencoded"
18
+ ContentTypeJSON = "application/json"
19
+ ContentTypeGraphQL = "application/graphql"
20
+ ContentTypeFormURLEncoded = "application/x-www-form-urlencoded"
21
+ ContentTypeMultipartFormData = "multipart/form-data"
19
22
)
20
23
24
+ type MultipartFile struct {
25
+ File multipart.File
26
+ Header * multipart.FileHeader
27
+ }
28
+
21
29
type Handler struct {
22
30
Schema * graphql.Schema
23
31
pretty bool
24
32
graphiql bool
25
33
playground bool
26
34
rootObjectFn RootObjectFn
35
+ maxMemory int64
27
36
}
28
37
type RequestOptions struct {
29
38
Query string `json:"query" url:"query" schema:"query"`
@@ -57,7 +66,7 @@ func getFromForm(values url.Values) *RequestOptions {
57
66
}
58
67
59
68
// RequestOptions Parses a http.Request into GraphQL request options struct
60
- func NewRequestOptions (r * http.Request ) * RequestOptions {
69
+ func NewRequestOptions (r * http.Request , maxMemory int64 ) * RequestOptions {
61
70
if reqOpt := getFromForm (r .URL .Query ()); reqOpt != nil {
62
71
return reqOpt
63
72
}
@@ -95,6 +104,89 @@ func NewRequestOptions(r *http.Request) *RequestOptions {
95
104
96
105
return & RequestOptions {}
97
106
107
+ case ContentTypeMultipartFormData :
108
+ if err := r .ParseMultipartForm (maxMemory ); err != nil {
109
+ // fmt.Printf("Parse Multipart Failed %v", err)
110
+ return & RequestOptions {}
111
+ }
112
+
113
+ // @TODO handle array case...
114
+
115
+ operationsParam := r .FormValue ("operations" )
116
+ var opts RequestOptions
117
+ if err := json .Unmarshal ([]byte (operationsParam ), & opts ); err != nil {
118
+ // fmt.Printf("Parse Operations Failed %v", err)
119
+ return & RequestOptions {}
120
+ }
121
+
122
+ mapParam := r .FormValue ("map" )
123
+ mapValues := make (map [string ]([]string ))
124
+ if len (mapParam ) != 0 {
125
+ if err := json .Unmarshal ([]byte (mapParam ), & mapValues ); err != nil {
126
+ // fmt.Printf("Parse map Failed %v", err)
127
+ return & RequestOptions {}
128
+ }
129
+ }
130
+
131
+ variables := opts
132
+
133
+ for key , value := range mapValues {
134
+ for _ , v := range value {
135
+ if file , header , err := r .FormFile (key ); err == nil {
136
+
137
+ // Now set the path in ther variables
138
+ var node interface {} = variables
139
+
140
+ parts := strings .Split (v , "." )
141
+ last := parts [len (parts )- 1 ]
142
+
143
+ for _ , vv := range parts [:len (parts )- 1 ] {
144
+ // fmt.Printf("Doing vv=%s type=%T parts=%v\n", vv, node, parts)
145
+ switch node .(type ) {
146
+ case RequestOptions :
147
+ if vv == "variables" {
148
+ node = opts .Variables
149
+ } else {
150
+ // panic("Invalid top level tag")
151
+ return & RequestOptions {}
152
+ }
153
+ case map [string ]interface {}:
154
+ node = node .(map [string ]interface {})[vv ]
155
+ case []interface {}:
156
+ if idx , err := strconv .ParseInt (vv , 10 , 64 ); err == nil {
157
+ node = node .([]interface {})[idx ]
158
+ } else {
159
+ // panic("Unable to lookup index")
160
+ return & RequestOptions {}
161
+ }
162
+ default :
163
+ // panic(fmt.Errorf("Unknown type %T", node))
164
+ return & RequestOptions {}
165
+ }
166
+ }
167
+
168
+ data := & MultipartFile {File : file , Header : header }
169
+
170
+ switch node .(type ) {
171
+ case map [string ]interface {}:
172
+ node .(map [string ]interface {})[last ] = data
173
+ case []interface {}:
174
+ if idx , err := strconv .ParseInt (last , 10 , 64 ); err == nil {
175
+ node .([]interface {})[idx ] = data
176
+ } else {
177
+ // panic("Unable to lookup index")
178
+ return & RequestOptions {}
179
+ }
180
+ default :
181
+ // panic(fmt.Errorf("Unknown last type %T", node))
182
+ return & RequestOptions {}
183
+ }
184
+ }
185
+ }
186
+ }
187
+
188
+ return & opts
189
+
98
190
case ContentTypeJSON :
99
191
fallthrough
100
192
default :
@@ -119,7 +211,7 @@ func NewRequestOptions(r *http.Request) *RequestOptions {
119
211
// user-provided context.
120
212
func (h * Handler ) ContextHandler (ctx context.Context , w http.ResponseWriter , r * http.Request ) {
121
213
// get query
122
- opts := NewRequestOptions (r )
214
+ opts := NewRequestOptions (r , h . maxMemory )
123
215
124
216
// execute graphql query
125
217
params := graphql.Params {
@@ -182,14 +274,15 @@ type Config struct {
182
274
GraphiQL bool
183
275
Playground bool
184
276
RootObjectFn RootObjectFn
277
+ MaxMemory int64
185
278
}
186
279
187
280
func NewConfig () * Config {
188
281
return & Config {
189
- Schema : nil ,
190
- Pretty : true ,
191
- GraphiQL : true ,
192
- Playground : false ,
282
+ Schema : nil ,
283
+ Pretty : true ,
284
+ GraphiQL : true ,
285
+ MaxMemory : 0 ,
193
286
}
194
287
}
195
288
@@ -201,11 +294,17 @@ func New(p *Config) *Handler {
201
294
panic ("undefined GraphQL schema" )
202
295
}
203
296
297
+ maxMemory := p .MaxMemory
298
+ if maxMemory == 0 {
299
+ maxMemory = 32 << 20 // 32MB
300
+ }
301
+
204
302
return & Handler {
205
303
Schema : p .Schema ,
206
304
pretty : p .Pretty ,
207
305
graphiql : p .GraphiQL ,
208
306
playground : p .Playground ,
209
307
rootObjectFn : p .RootObjectFn ,
308
+ maxMemory : maxMemory ,
210
309
}
211
310
}
0 commit comments