github.com/geneva/gqlgen@v0.17.7-0.20230801155730-7b9317164836/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/geneva/gqlgen/graphql"
    11  	"github.com/geneva/gqlgen/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  	// Map of all headers that are added to graphql response. If not
    20  	// set, only one header: Content-Type: application/json will be set.
    21  	ResponseHeaders map[string][]string
    22  }
    23  
    24  var _ graphql.Transport = GET{}
    25  
    26  func (h GET) Supports(r *http.Request) bool {
    27  	if r.Header.Get("Upgrade") != "" {
    28  		return false
    29  	}
    30  
    31  	return r.Method == "GET"
    32  }
    33  
    34  func (h GET) Do(w http.ResponseWriter, r *http.Request, exec graphql.GraphExecutor) {
    35  	query, err := url.ParseQuery(r.URL.RawQuery)
    36  	if err != nil {
    37  		w.WriteHeader(http.StatusBadRequest)
    38  		writeJsonError(w, err.Error())
    39  		return
    40  	}
    41  	writeHeaders(w, h.ResponseHeaders)
    42  
    43  	raw := &graphql.RawParams{
    44  		Query:         query.Get("query"),
    45  		OperationName: query.Get("operationName"),
    46  		Headers:       r.Header,
    47  	}
    48  	raw.ReadTime.Start = graphql.Now()
    49  
    50  	if variables := query.Get("variables"); variables != "" {
    51  		if err := jsonDecode(strings.NewReader(variables), &raw.Variables); err != nil {
    52  			w.WriteHeader(http.StatusBadRequest)
    53  			writeJsonError(w, "variables could not be decoded")
    54  			return
    55  		}
    56  	}
    57  
    58  	if extensions := query.Get("extensions"); extensions != "" {
    59  		if err := jsonDecode(strings.NewReader(extensions), &raw.Extensions); err != nil {
    60  			w.WriteHeader(http.StatusBadRequest)
    61  			writeJsonError(w, "extensions could not be decoded")
    62  			return
    63  		}
    64  	}
    65  
    66  	raw.ReadTime.End = graphql.Now()
    67  
    68  	rc, gqlError := exec.CreateOperationContext(r.Context(), raw)
    69  	if gqlError != nil {
    70  		w.WriteHeader(statusFor(gqlError))
    71  		resp := exec.DispatchError(graphql.WithOperationContext(r.Context(), rc), gqlError)
    72  		writeJson(w, resp)
    73  		return
    74  	}
    75  	op := rc.Doc.Operations.ForName(rc.OperationName)
    76  	if op.Operation != ast.Query {
    77  		w.WriteHeader(http.StatusNotAcceptable)
    78  		writeJsonError(w, "GET requests only allow query operations")
    79  		return
    80  	}
    81  
    82  	responses, ctx := exec.DispatchOperation(r.Context(), rc)
    83  	writeJson(w, responses(ctx))
    84  }
    85  
    86  func jsonDecode(r io.Reader, val interface{}) error {
    87  	dec := json.NewDecoder(r)
    88  	dec.UseNumber()
    89  	return dec.Decode(val)
    90  }
    91  
    92  func statusFor(errs gqlerror.List) int {
    93  	switch errcode.GetErrorKind(errs) {
    94  	case errcode.KindProtocol:
    95  		return http.StatusUnprocessableEntity
    96  	default:
    97  		return http.StatusOK
    98  	}
    99  }