@@ -3,8 +3,10 @@ package handler
33import (
44 "encoding/json"
55 "io/ioutil"
6+ "mime/multipart"
67 "net/http"
78 "net/url"
9+ "strconv"
810 "strings"
911
1012 "github.com/graphql-go/graphql"
@@ -13,17 +15,24 @@ import (
1315)
1416
1517const (
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"
1922)
2023
24+ type MultipartFile struct {
25+ File multipart.File
26+ Header * multipart.FileHeader
27+ }
28+
2129type Handler struct {
2230 Schema * graphql.Schema
2331 pretty bool
2432 graphiql bool
2533 playground bool
2634 rootObjectFn RootObjectFn
35+ maxMemory int64
2736}
2837type RequestOptions struct {
2938 Query string `json:"query" url:"query" schema:"query"`
@@ -57,7 +66,7 @@ func getFromForm(values url.Values) *RequestOptions {
5766}
5867
5968// 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 {
6170 if reqOpt := getFromForm (r .URL .Query ()); reqOpt != nil {
6271 return reqOpt
6372 }
@@ -95,6 +104,89 @@ func NewRequestOptions(r *http.Request) *RequestOptions {
95104
96105 return & RequestOptions {}
97106
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+
98190 case ContentTypeJSON :
99191 fallthrough
100192 default :
@@ -119,7 +211,7 @@ func NewRequestOptions(r *http.Request) *RequestOptions {
119211// user-provided context.
120212func (h * Handler ) ContextHandler (ctx context.Context , w http.ResponseWriter , r * http.Request ) {
121213 // get query
122- opts := NewRequestOptions (r )
214+ opts := NewRequestOptions (r , h . maxMemory )
123215
124216 // execute graphql query
125217 params := graphql.Params {
@@ -182,14 +274,15 @@ type Config struct {
182274 GraphiQL bool
183275 Playground bool
184276 RootObjectFn RootObjectFn
277+ MaxMemory int64
185278}
186279
187280func NewConfig () * Config {
188281 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 ,
193286 }
194287}
195288
@@ -201,11 +294,17 @@ func New(p *Config) *Handler {
201294 panic ("undefined GraphQL schema" )
202295 }
203296
297+ maxMemory := p .MaxMemory
298+ if maxMemory == 0 {
299+ maxMemory = 32 << 20 // 32MB
300+ }
301+
204302 return & Handler {
205303 Schema : p .Schema ,
206304 pretty : p .Pretty ,
207305 graphiql : p .GraphiQL ,
208306 playground : p .Playground ,
209307 rootObjectFn : p .RootObjectFn ,
308+ maxMemory : maxMemory ,
210309 }
211310}
0 commit comments