Repository: jensneuse/graphql-go-tools
Pull request #128 (2019-12-02 13:05:16)

Feature/add suppert for header passing
This PR will add support to extract uri, host and headers from the request and pass them on as variables on the execution context.
Closes #54 


Patch:
diff --git a/pkg/execution/handler.go b/pkg/execution/handler.go
index 37bcde5e..a1a4fb49 100644
--- a/pkg/execution/handler.go
+++ b/pkg/execution/handler.go
@@ -66,15 +66,15 @@ func (h *Handler) RenderGraphQLDefinitions(out io.Writer) error {
 }
 
 type GraphqlRequest struct {
-	OperationName string                     `json:"operation_name"`
+	OperationName string          `json:"operation_name"`
 	Variables     json.RawMessage `json:"variables"`
-	Query         string                     `json:"query"`
+	Query         string          `json:"query"`
 }
 
-func (h *Handler) Handle(data []byte) (executor *Executor, node RootNode, ctx Context, err error) {
+func (h *Handler) Handle(requestData, extraVariables []byte) (executor *Executor, node RootNode, ctx Context, err error) {
 
 	var graphqlRequest GraphqlRequest
-	err = json.Unmarshal(data, &graphqlRequest)
+	err = json.Unmarshal(requestData, &graphqlRequest)
 	if err != nil {
 		return
 	}
@@ -85,7 +85,7 @@ func (h *Handler) Handle(data []byte) (executor *Executor, node RootNode, ctx Co
 		return
 	}
 
-	variables := h.VariablesFromRequest(graphqlRequest)
+	variables := h.VariablesFromJson(graphqlRequest.Variables, extraVariables)
 
 	planner := NewPlanner(h.resolverDefinitions(&report))
 	if report.HasErrors() {
@@ -129,12 +129,14 @@ func (h *Handler) Handle(data []byte) (executor *Executor, node RootNode, ctx Co
 	return executor, plan, ctx, err
 }
 
-func (h *Handler) VariablesFromRequest(request GraphqlRequest) Variables {
-	variables := make(Variables, 4)
-	_ = jsonparser.ObjectEach(request.Variables, func(key []byte, value []byte, dataType jsonparser.ValueType, offset int) error {
-		variables[xxhash.Sum64(key)] = value
-		return nil
-	})
+func (h *Handler) VariablesFromJson(inputJson ...[]byte) Variables {
+	variables := map[uint64][]byte{}
+	for i := range inputJson {
+		_ = jsonparser.ObjectEach(inputJson[i], func(key []byte, value []byte, dataType jsonparser.ValueType, offset int) error {
+			variables[xxhash.Sum64(key)] = value
+			return nil
+		})
+	}
 	return variables
 }
 
diff --git a/pkg/http/handler.go b/pkg/http/handler.go
index 48de4026..8cd2aa81 100644
--- a/pkg/http/handler.go
+++ b/pkg/http/handler.go
@@ -1,9 +1,11 @@
 package http
 
 import (
+	"encoding/json"
 	"github.com/gobwas/ws"
 	"github.com/jensneuse/graphql-go-tools/pkg/execution"
 	"go.uber.org/zap"
+	"io"
 	"net/http"
 )
 
@@ -41,7 +43,7 @@ func (g *GraphQLHTTPRequestHandler) upgradeWithNewGoroutine(w http.ResponseWrite
 	if err != nil {
 		return err
 	}
-	go g.handleWebsocket(conn)
+	go g.handleWebsocket(r, conn)
 	return nil
 }
 
@@ -53,3 +55,21 @@ func (g *GraphQLHTTPRequestHandler) isWebsocketUpgrade(r *http.Request) bool {
 	}
 	return false
 }
+
+func (g *GraphQLHTTPRequestHandler) extraVariables(r *http.Request, out io.Writer) error {
+	headers := map[string]string{}
+	for key := range r.Header {
+		headers[key] = r.Header.Get(key)
+	}
+
+	extra := map[string]interface{}{
+		"request": map[string]interface{}{
+			"uri":     r.RequestURI,
+			"method":  r.Method,
+			"host":    r.Host,
+			"headers": headers,
+		},
+	}
+
+	return json.NewEncoder(out).Encode(extra)
+}
diff --git a/pkg/http/http.go b/pkg/http/http.go
index c028e154..db43d7d3 100644
--- a/pkg/http/http.go
+++ b/pkg/http/http.go
@@ -16,7 +16,18 @@ func (g *GraphQLHTTPRequestHandler) handleHTTP(w http.ResponseWriter, r *http.Re
 		w.WriteHeader(http.StatusBadRequest)
 		return
 	}
-	executor, rootNode, ctx, err := g.executionHandler.Handle(data)
+
+	extra := &bytes.Buffer{}
+	err = g.extraVariables(r,extra)
+	if err != nil {
+		g.log.Error("executionHandler.Handle.json.Marshal(extra)",
+			zap.Error(err),
+		)
+		w.WriteHeader(http.StatusBadRequest)
+		return
+	}
+
+	executor, rootNode, ctx, err := g.executionHandler.Handle(data, extra.Bytes())
 	if err != nil {
 		g.log.Error("executionHandler.Handle",
 			zap.Error(err),
diff --git a/pkg/http/ws.go b/pkg/http/ws.go
index 33c3aa08..b86e6608 100644
--- a/pkg/http/ws.go
+++ b/pkg/http/ws.go
@@ -9,6 +9,7 @@ import (
 	"github.com/jensneuse/graphql-go-tools/pkg/execution"
 	"go.uber.org/zap"
 	"net"
+	"net/http"
 )
 
 const (
@@ -30,7 +31,7 @@ type WebsocketMessage struct {
 	Payload json.RawMessage `json:"payload"`
 }
 
-func (g *GraphQLHTTPRequestHandler) handleWebsocket(conn net.Conn) {
+func (g *GraphQLHTTPRequestHandler) handleWebsocket(r *http.Request, conn net.Conn) {
 	defer conn.Close()
 
 	subscriptions := map[string]context.CancelFunc{}
@@ -77,7 +78,7 @@ func (g *GraphQLHTTPRequestHandler) handleWebsocket(conn net.Conn) {
 		case START:
 			ctx, cancel := context.WithCancel(context.Background())
 			subscriptions[message.Id] = cancel
-			go g.startSubscription(ctx, message.Payload, conn, op, message.Id)
+			go g.startSubscription(r, ctx, message.Payload, conn, op, message.Id)
 		case STOP:
 			cancel, ok := subscriptions[message.Id]
 			if !ok {
@@ -99,8 +100,12 @@ func (g *GraphQLHTTPRequestHandler) sendAck(conn net.Conn, op ws.OpCode) error {
 	return wsutil.WriteServerMessage(conn, op, data)
 }
 
-func (g *GraphQLHTTPRequestHandler) startSubscription(ctx context.Context, data []byte, conn net.Conn, op ws.OpCode, id string) {
-	executor, node, executionContext, err := g.executionHandler.Handle(data)
+func (g *GraphQLHTTPRequestHandler) startSubscription(r *http.Request, ctx context.Context, data []byte, conn net.Conn, op ws.OpCode, id string) {
+
+	extra := &bytes.Buffer{}
+	_ = g.extraVariables(r, extra)
+
+	executor, node, executionContext, err := g.executionHandler.Handle(data, extra.Bytes())
 	if err != nil {
 		g.log.Error("GraphQLHTTPRequestHandler.startSubscription.executionHandler.Handle",
 			zap.Error(err),


Test patch:
diff --git a/pkg/execution/handler_test.go b/pkg/execution/handler_test.go
index 6599e137..4c2bd378 100644
--- a/pkg/execution/handler_test.go
+++ b/pkg/execution/handler_test.go
@@ -52,10 +52,14 @@ func TestHandler_VariablesFromRequest(t *testing.T) {
 	request := GraphqlRequest{
 		Variables: []byte(`{"foo":"bar"}`),
 	}
-	variables := handler.VariablesFromRequest(request)
+
+	extra := []byte(`{"request":{"headers":{"Authorization":"Bearer foo123"}}}`)
+
+	variables := handler.VariablesFromJson(request.Variables,extra)
 
 	for key,value := range map[string]string{
 		"foo": "bar",
+		"request": `{"headers":{"Authorization":"Bearer foo123"}}`,
 	}{
 		got := string(variables[xxhash.Sum64String(key)])
 		want := value