Skip to content

Commit

Permalink
graphql: always return 400 if errors are present in the response (eth…
Browse files Browse the repository at this point in the history
…ereum#21882)

* Make sure to return 400 when errors are present in the response

* graphql: use less memory in chainconfig for tests

Co-authored-by: Martin Holst Swende <[email protected]>
  • Loading branch information
atoulme and holiman authored Nov 25, 2020
1 parent c92faee commit f59ed35
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 4 deletions.
52 changes: 50 additions & 2 deletions graphql/graphql_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,13 @@ import (
"net/http"
"strings"
"testing"
"time"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus/ethash"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/miner"
"github.com/ethereum/go-ethereum/node"
"github.com/stretchr/testify/assert"
)
Expand Down Expand Up @@ -61,6 +66,7 @@ func TestGraphQLHTTPOnSamePort_GQLRequest_Successful(t *testing.T) {
t.Fatalf("could not read from response body: %v", err)
}
expected := "{\"data\":{\"block\":{\"number\":\"0x0\"}}}"
assert.Equal(t, 200, resp.StatusCode)
assert.Equal(t, expected, string(bodyBytes))
}

Expand Down Expand Up @@ -90,6 +96,32 @@ func TestGraphQLHTTPOnSamePort_GQLRequest_Unsuccessful(t *testing.T) {
assert.Equal(t, "404 page not found\n", string(bodyBytes))
}

// Tests that 400 is returned when an invalid RPC request is made.
func TestGraphQL_BadRequest(t *testing.T) {
stack := createNode(t, true)
defer stack.Close()
// start node
if err := stack.Start(); err != nil {
t.Fatalf("could not start node: %v", err)
}
// create http request
body := strings.NewReader("{\"query\": \"{bleh{number}}\",\"variables\": null}")
gqlReq, err := http.NewRequest(http.MethodGet, fmt.Sprintf("http://%s/graphql", "127.0.0.1:9393"), body)
if err != nil {
t.Error("could not issue new http request ", err)
}
gqlReq.Header.Set("Content-Type", "application/json")
// read from response
resp := doHTTPRequest(t, gqlReq)
bodyBytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
t.Fatalf("could not read from response body: %v", err)
}
expected := "{\"errors\":[{\"message\":\"Cannot query field \\\"bleh\\\" on type \\\"Query\\\".\",\"locations\":[{\"line\":1,\"column\":2}]}]}"
assert.Equal(t, expected, string(bodyBytes))
assert.Equal(t, 400, resp.StatusCode)
}

func createNode(t *testing.T, gqlEnabled bool) *node.Node {
stack, err := node.New(&node.Config{
HTTPHost: "127.0.0.1",
Expand All @@ -110,8 +142,24 @@ func createNode(t *testing.T, gqlEnabled bool) *node.Node {
}

func createGQLService(t *testing.T, stack *node.Node, endpoint string) {
// create backend
ethBackend, err := eth.New(stack, &eth.DefaultConfig)
// create backend (use a config which is light on mem consumption)
ethConf := &eth.Config{
Genesis: core.DeveloperGenesisBlock(15, common.Address{}),
Miner: miner.Config{
Etherbase: common.HexToAddress("0xaabb"),
},
Ethash: ethash.Config{
PowMode: ethash.ModeTest,
},
NetworkId: 1337,
TrieCleanCache: 5,
TrieCleanCacheJournal: "triecache",
TrieCleanCacheRejournal: 60 * time.Minute,
TrieDirtyCache: 5,
TrieTimeout: 60 * time.Minute,
SnapshotCache: 5,
}
ethBackend, err := eth.New(stack, ethConf)
if err != nil {
t.Fatalf("could not create eth backend: %v", err)
}
Expand Down
36 changes: 34 additions & 2 deletions graphql/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,44 @@
package graphql

import (
"encoding/json"
"net/http"

"github.com/ethereum/go-ethereum/internal/ethapi"
"github.com/ethereum/go-ethereum/node"
"github.com/graph-gophers/graphql-go"
"github.com/graph-gophers/graphql-go/relay"
)

type handler struct {
Schema *graphql.Schema
}

func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
var params struct {
Query string `json:"query"`
OperationName string `json:"operationName"`
Variables map[string]interface{} `json:"variables"`
}
if err := json.NewDecoder(r.Body).Decode(&params); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}

response := h.Schema.Exec(r.Context(), params.Query, params.OperationName, params.Variables)
responseJSON, err := json.Marshal(response)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
if len(response.Errors) > 0 {
w.WriteHeader(http.StatusBadRequest)
}

w.Header().Set("Content-Type", "application/json")
w.Write(responseJSON)

}

// New constructs a new GraphQL service instance.
func New(stack *node.Node, backend ethapi.Backend, cors, vhosts []string) error {
if backend == nil {
Expand All @@ -41,7 +73,7 @@ func newHandler(stack *node.Node, backend ethapi.Backend, cors, vhosts []string)
if err != nil {
return err
}
h := &relay.Handler{Schema: s}
h := handler{Schema: s}
handler := node.NewHTTPHandlerStack(h, cors, vhosts)

stack.RegisterHandler("GraphQL UI", "/graphql/ui", GraphiQL{})
Expand Down

0 comments on commit f59ed35

Please sign in to comment.