8
8
"net/http"
9
9
"strconv"
10
10
"strings"
11
+ "sync"
11
12
12
13
"github.com/nautilus/graphql"
13
14
)
@@ -27,6 +28,8 @@ type HTTPOperation struct {
27
28
} `json:"extensions"`
28
29
}
29
30
31
+ type setResultFunc func (r map [string ]interface {})
32
+
30
33
func formatErrors (err error ) map [string ]interface {} {
31
34
return formatErrorsWithCode (nil , err , "UNKNOWN_ERROR" )
32
35
}
@@ -70,12 +73,14 @@ func (g *Gateway) GraphQLHandler(w http.ResponseWriter, r *http.Request) {
70
73
/// Handle the operations regardless of the request method
71
74
72
75
// we have to respond to each operation in the right order
73
- results := []map [string ]interface {}{}
76
+ results := make ([]map [string ]interface {}, len (operations ))
77
+ opWg := new (sync.WaitGroup )
78
+ opMutex := new (sync.Mutex )
74
79
75
80
// the status code to report
76
81
statusCode := http .StatusOK
77
82
78
- for _ , operation := range operations {
83
+ for opNum , operation := range operations {
79
84
// there might be a query plan cache key embedded in the operation
80
85
cacheKey := ""
81
86
if operation .Extensions .QueryPlanCache != nil {
@@ -85,10 +90,8 @@ func (g *Gateway) GraphQLHandler(w http.ResponseWriter, r *http.Request) {
85
90
// if there is no query or cache key
86
91
if operation .Query == "" && cacheKey == "" {
87
92
statusCode = http .StatusUnprocessableEntity
88
- results = append (
89
- results ,
90
- formatErrorsWithCode (nil , errors .New ("could not find query body" ), "BAD_USER_INPUT" ),
91
- )
93
+ results [opNum ] = formatErrorsWithCode (nil , errors .New ("could not find query body" ), "BAD_USER_INPUT" )
94
+
92
95
continue
93
96
}
94
97
@@ -116,32 +119,12 @@ func (g *Gateway) GraphQLHandler(w http.ResponseWriter, r *http.Request) {
116
119
return
117
120
}
118
121
119
- // fire the query with the request context passed through to execution
120
- result , err := g .Execute (requestContext , plan )
121
- if err != nil {
122
- results = append (results , formatErrorsWithCode (result , err , "INTERNAL_SERVER_ERROR" ))
123
-
124
- continue
125
- }
126
-
127
- // the result for this operation
128
- payload := map [string ]interface {}{"data" : result }
129
-
130
- // if there was a cache key associated with this query
131
- if requestContext .CacheKey != "" {
132
- // embed the cache key in the response
133
- payload ["extensions" ] = map [string ]interface {}{
134
- "persistedQuery" : map [string ]interface {}{
135
- "sha265Hash" : requestContext .CacheKey ,
136
- "version" : "1" ,
137
- },
138
- }
139
- }
140
-
141
- // add this result to the list
142
- results = append (results , payload )
122
+ opWg .Add (1 )
123
+ go g .executeRequest (requestContext , plan , opWg , g .setResultFunc (opNum , results , opMutex ))
143
124
}
144
125
126
+ opWg .Wait ()
127
+
145
128
// the final result depends on whether we are executing in batch mode or not
146
129
var finalResponse interface {}
147
130
if batchMode {
@@ -165,6 +148,43 @@ func (g *Gateway) GraphQLHandler(w http.ResponseWriter, r *http.Request) {
165
148
emitResponse (w , statusCode , string (response ))
166
149
}
167
150
151
+ func (g * Gateway ) setResultFunc (opNum int , results []map [string ]interface {}, opMutex * sync.Mutex ) setResultFunc {
152
+ return func (r map [string ]interface {}) {
153
+ opMutex .Lock ()
154
+ defer opMutex .Unlock ()
155
+ results [opNum ] = r
156
+ }
157
+ }
158
+
159
+ func (g * Gateway ) executeRequest (requestContext * RequestContext , plan QueryPlanList , opWg * sync.WaitGroup , setResult setResultFunc ) {
160
+ defer opWg .Done ()
161
+
162
+ // fire the query with the request context passed through to execution
163
+ result , err := g .Execute (requestContext , plan )
164
+ if err != nil {
165
+ setResult (formatErrorsWithCode (result , err , "INTERNAL_SERVER_ERROR" ))
166
+
167
+ return
168
+ }
169
+
170
+ // the result for this operation
171
+ payload := map [string ]interface {}{"data" : result }
172
+
173
+ // if there was a cache key associated with this query
174
+ if requestContext .CacheKey != "" {
175
+ // embed the cache key in the response
176
+ payload ["extensions" ] = map [string ]interface {}{
177
+ "persistedQuery" : map [string ]interface {}{
178
+ "sha265Hash" : requestContext .CacheKey ,
179
+ "version" : "1" ,
180
+ },
181
+ }
182
+ }
183
+
184
+ // add this result to the list
185
+ setResult (payload )
186
+ }
187
+
168
188
// Parses request to operations (single or batch mode).
169
189
// Returns an error and an error status code if the request is invalid.
170
190
func parseRequest (r * http.Request ) (operations []* HTTPOperation , batchMode bool , errStatusCode int , payloadErr error ) {
0 commit comments