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