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