...
1 package transport
2
3 import (
4 "encoding/json"
5 "io"
6 "net/http"
7 "net/url"
8 "strings"
9
10 "github.com/vektah/gqlparser/v2/ast"
11 "github.com/vektah/gqlparser/v2/gqlerror"
12
13 "github.com/99designs/gqlgen/graphql"
14 "github.com/99designs/gqlgen/graphql/errcode"
15 )
16
17
18
19 type GET struct {
20
21
22 ResponseHeaders map[string][]string
23 }
24
25 var _ graphql.Transport = GET{}
26
27 func (h GET) Supports(r *http.Request) bool {
28 if r.Header.Get("Upgrade") != "" {
29 return false
30 }
31
32 return r.Method == "GET"
33 }
34
35 func (h GET) Do(w http.ResponseWriter, r *http.Request, exec graphql.GraphExecutor) {
36 query, err := url.ParseQuery(r.URL.RawQuery)
37 if err != nil {
38 w.WriteHeader(http.StatusBadRequest)
39 writeJsonError(w, err.Error())
40 return
41 }
42 writeHeaders(w, h.ResponseHeaders)
43
44 raw := &graphql.RawParams{
45 Query: query.Get("query"),
46 OperationName: query.Get("operationName"),
47 Headers: r.Header,
48 }
49 raw.ReadTime.Start = graphql.Now()
50
51 if variables := query.Get("variables"); variables != "" {
52 if err := jsonDecode(strings.NewReader(variables), &raw.Variables); err != nil {
53 w.WriteHeader(http.StatusBadRequest)
54 writeJsonError(w, "variables could not be decoded")
55 return
56 }
57 }
58
59 if extensions := query.Get("extensions"); extensions != "" {
60 if err := jsonDecode(strings.NewReader(extensions), &raw.Extensions); err != nil {
61 w.WriteHeader(http.StatusBadRequest)
62 writeJsonError(w, "extensions could not be decoded")
63 return
64 }
65 }
66
67 raw.ReadTime.End = graphql.Now()
68
69 rc, gqlError := exec.CreateOperationContext(r.Context(), raw)
70 if gqlError != nil {
71 w.WriteHeader(statusFor(gqlError))
72 resp := exec.DispatchError(graphql.WithOperationContext(r.Context(), rc), gqlError)
73 writeJson(w, resp)
74 return
75 }
76 op := rc.Doc.Operations.ForName(rc.OperationName)
77 if op.Operation != ast.Query {
78 w.WriteHeader(http.StatusNotAcceptable)
79 writeJsonError(w, "GET requests only allow query operations")
80 return
81 }
82
83 responses, ctx := exec.DispatchOperation(r.Context(), rc)
84 writeJson(w, responses(ctx))
85 }
86
87 func jsonDecode(r io.Reader, val interface{}) error {
88 dec := json.NewDecoder(r)
89 dec.UseNumber()
90 return dec.Decode(val)
91 }
92
93 func statusFor(errs gqlerror.List) int {
94 switch errcode.GetErrorKind(errs) {
95 case errcode.KindProtocol:
96 return http.StatusUnprocessableEntity
97 default:
98 return http.StatusOK
99 }
100 }
101
View as plain text