git.sr.ht/~sircmpwn/gqlgen@v0.0.0-20200522192042-c84d29a1c940/graphql/handler/transport/http_get.go (about)

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