diff options
Diffstat (limited to 'vendor/github.com/graphql-go/handler/handler.go')
-rw-r--r-- | vendor/github.com/graphql-go/handler/handler.go | 189 |
1 files changed, 189 insertions, 0 deletions
diff --git a/vendor/github.com/graphql-go/handler/handler.go b/vendor/github.com/graphql-go/handler/handler.go new file mode 100644 index 00000000..cfcb1ca9 --- /dev/null +++ b/vendor/github.com/graphql-go/handler/handler.go @@ -0,0 +1,189 @@ +package handler + +import ( + "encoding/json" + "io/ioutil" + "net/http" + "net/url" + "strings" + + "github.com/graphql-go/graphql" + + "context" +) + +const ( + ContentTypeJSON = "application/json" + ContentTypeGraphQL = "application/graphql" + ContentTypeFormURLEncoded = "application/x-www-form-urlencoded" +) + +type Handler struct { + Schema *graphql.Schema + pretty bool + graphiql bool +} +type RequestOptions struct { + Query string `json:"query" url:"query" schema:"query"` + Variables map[string]interface{} `json:"variables" url:"variables" schema:"variables"` + OperationName string `json:"operationName" url:"operationName" schema:"operationName"` +} + +// a workaround for getting`variables` as a JSON string +type requestOptionsCompatibility struct { + Query string `json:"query" url:"query" schema:"query"` + Variables string `json:"variables" url:"variables" schema:"variables"` + OperationName string `json:"operationName" url:"operationName" schema:"operationName"` +} + +func getFromForm(values url.Values) *RequestOptions { + query := values.Get("query") + if query != "" { + // get variables map + variables := make(map[string]interface{}, len(values)) + variablesStr := values.Get("variables") + json.Unmarshal([]byte(variablesStr), &variables) + + return &RequestOptions{ + Query: query, + Variables: variables, + OperationName: values.Get("operationName"), + } + } + + return nil +} + +// RequestOptions Parses a http.Request into GraphQL request options struct +func NewRequestOptions(r *http.Request) *RequestOptions { + if reqOpt := getFromForm(r.URL.Query()); reqOpt != nil { + return reqOpt + } + + if r.Method != "POST" { + return &RequestOptions{} + } + + if r.Body == nil { + return &RequestOptions{} + } + + // TODO: improve Content-Type handling + contentTypeStr := r.Header.Get("Content-Type") + contentTypeTokens := strings.Split(contentTypeStr, ";") + contentType := contentTypeTokens[0] + + switch contentType { + case ContentTypeGraphQL: + body, err := ioutil.ReadAll(r.Body) + if err != nil { + return &RequestOptions{} + } + return &RequestOptions{ + Query: string(body), + } + case ContentTypeFormURLEncoded: + if err := r.ParseForm(); err != nil { + return &RequestOptions{} + } + + if reqOpt := getFromForm(r.PostForm); reqOpt != nil { + return reqOpt + } + + return &RequestOptions{} + + case ContentTypeJSON: + fallthrough + default: + var opts RequestOptions + body, err := ioutil.ReadAll(r.Body) + if err != nil { + return &opts + } + err = json.Unmarshal(body, &opts) + if err != nil { + // Probably `variables` was sent as a string instead of an object. + // So, we try to be polite and try to parse that as a JSON string + var optsCompatible requestOptionsCompatibility + json.Unmarshal(body, &optsCompatible) + json.Unmarshal([]byte(optsCompatible.Variables), &opts.Variables) + } + return &opts + } +} + +// ContextHandler provides an entrypoint into executing graphQL queries with a +// user-provided context. +func (h *Handler) ContextHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) { + // get query + opts := NewRequestOptions(r) + + // execute graphql query + params := graphql.Params{ + Schema: *h.Schema, + RequestString: opts.Query, + VariableValues: opts.Variables, + OperationName: opts.OperationName, + Context: ctx, + } + result := graphql.Do(params) + + if h.graphiql { + acceptHeader := r.Header.Get("Accept") + _, raw := r.URL.Query()["raw"] + if !raw && !strings.Contains(acceptHeader, "application/json") && strings.Contains(acceptHeader, "text/html") { + renderGraphiQL(w, params) + return + } + } + + // use proper JSON Header + w.Header().Add("Content-Type", "application/json; charset=utf-8") + + if h.pretty { + w.WriteHeader(http.StatusOK) + buff, _ := json.MarshalIndent(result, "", "\t") + + w.Write(buff) + } else { + w.WriteHeader(http.StatusOK) + buff, _ := json.Marshal(result) + + w.Write(buff) + } +} + +// ServeHTTP provides an entrypoint into executing graphQL queries. +func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + h.ContextHandler(r.Context(), w, r) +} + +type Config struct { + Schema *graphql.Schema + Pretty bool + GraphiQL bool +} + +func NewConfig() *Config { + return &Config{ + Schema: nil, + Pretty: true, + GraphiQL: true, + } +} + +func New(p *Config) *Handler { + if p == nil { + p = NewConfig() + } + if p.Schema == nil { + panic("undefined GraphQL schema") + } + + return &Handler{ + Schema: p.Schema, + pretty: p.Pretty, + graphiql: p.GraphiQL, + } +} |