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  }