Skip to content

Commit

Permalink
Merge pull request #92 from graphql-go/graphql-playground-endpoint-su…
Browse files Browse the repository at this point in the history
…pport

Adds support for setting GraphQL Playground endpoints.
  • Loading branch information
chris-ramon authored May 17, 2024
2 parents f96ffdd + 5f4c951 commit 62f4eb5
Show file tree
Hide file tree
Showing 5 changed files with 140 additions and 11 deletions.
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
module github.com/graphql-go/handler

go 1.14

require github.com/graphql-go/graphql v0.8.1 // indirect
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
github.com/graphql-go/graphql v0.8.1 h1:p7/Ou/WpmulocJeEx7wjQy611rtXGQaAcXGqanuMMgc=
github.com/graphql-go/graphql v0.8.1/go.mod h1:nKiHzRM0qopJEwCITUuIsxk9PlVlwIiiI8pnJEhordQ=
9 changes: 3 additions & 6 deletions graphcoolPlayground.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package handler

import (
"fmt"
"html/template"
"net/http"
)
Expand All @@ -14,7 +13,7 @@ type playgroundData struct {
}

// renderPlayground renders the Playground GUI
func renderPlayground(w http.ResponseWriter, r *http.Request) {
func renderPlayground(w http.ResponseWriter, r *http.Request, endpoint string, subscriptionEndpoint string) {
t := template.New("Playground")
t, err := t.Parse(graphcoolPlaygroundTemplate)
if err != nil {
Expand All @@ -24,16 +23,14 @@ func renderPlayground(w http.ResponseWriter, r *http.Request) {

d := playgroundData{
PlaygroundVersion: graphcoolPlaygroundVersion,
Endpoint: r.URL.Path,
SubscriptionEndpoint: fmt.Sprintf("ws://%v/subscriptions", r.Host),
Endpoint: endpoint,
SubscriptionEndpoint: subscriptionEndpoint,
SetTitle: true,
}
err = t.ExecuteTemplate(w, "index", d)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}

return
}

const graphcoolPlaygroundVersion = "1.5.2"
Expand Down
28 changes: 23 additions & 5 deletions handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package handler

import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/url"
Expand All @@ -27,6 +28,7 @@ type Handler struct {
pretty bool
graphiql bool
playground bool
playgroundConfig *PlaygroundConfig
rootObjectFn RootObjectFn
resultCallbackFn ResultCallbackFn
formatErrorFn func(err error) gqlerrors.FormattedError
Expand Down Expand Up @@ -162,7 +164,15 @@ func (h *Handler) ContextHandler(ctx context.Context, w http.ResponseWriter, r *
acceptHeader := r.Header.Get("Accept")
_, raw := r.URL.Query()["raw"]
if !raw && !strings.Contains(acceptHeader, "application/json") && strings.Contains(acceptHeader, "text/html") {
renderPlayground(w, r)

endpoint := r.URL.Path
subscriptionEndpoint := fmt.Sprintf("ws://%v/subscriptions", r.Host)
if h.playgroundConfig != nil {
endpoint = h.playgroundConfig.Endpoint
subscriptionEndpoint = h.playgroundConfig.SubscriptionEndpoint
}

renderPlayground(w, r, endpoint, subscriptionEndpoint)
return
}
}
Expand Down Expand Up @@ -196,22 +206,29 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// RootObjectFn allows a user to generate a RootObject per request
type RootObjectFn func(ctx context.Context, r *http.Request) map[string]interface{}

type PlaygroundConfig struct {
Endpoint string
SubscriptionEndpoint string
}

type Config struct {
Schema *graphql.Schema
Pretty bool
GraphiQL bool
Playground bool
PlaygroundConfig *PlaygroundConfig
RootObjectFn RootObjectFn
ResultCallbackFn ResultCallbackFn
FormatErrorFn func(err error) gqlerrors.FormattedError
}

func NewConfig() *Config {
return &Config{
Schema: nil,
Pretty: true,
GraphiQL: true,
Playground: false,
Schema: nil,
Pretty: true,
GraphiQL: true,
Playground: false,
PlaygroundConfig: nil,
}
}

Expand All @@ -229,6 +246,7 @@ func New(p *Config) *Handler {
pretty: p.Pretty,
graphiql: p.GraphiQL,
playground: p.Playground,
playgroundConfig: p.PlaygroundConfig,
rootObjectFn: p.RootObjectFn,
resultCallbackFn: p.ResultCallbackFn,
formatErrorFn: p.FormatErrorFn,
Expand Down
110 changes: 110 additions & 0 deletions handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -290,3 +290,113 @@ func TestHandler_BasicQuery_WithFormatErrorFn(t *testing.T) {
t.Fatalf("wrong result, graphql result diff: %v", testutil.Diff(expected, result))
}
}

func TestPlaygroundWithDefaultConfig(t *testing.T) {
query := graphql.NewObject(graphql.ObjectConfig{
Name: "Query",
Fields: graphql.Fields{
"ping": &graphql.Field{
Name: "ping",
Type: graphql.String,
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
return "OK", nil
},
},
},
})

schema, err := graphql.NewSchema(graphql.SchemaConfig{
Query: query,
})
if err != nil {
t.Fatal(err)
}

req, err := http.NewRequest("GET", "/graphql", nil)
req.Header.Set("Accept", "text/html")
if err != nil {
t.Fatal(err)
}

h := handler.New(&handler.Config{
Schema: &schema,
Playground: true,
})

resp := httptest.NewRecorder()
h.ContextHandler(context.Background(), resp, req)

if resp.Code != http.StatusOK {
t.Fatalf("unexpected server response %v", resp.Code)
}

expectedBodyContains := []string{
"GraphQL Playground",
`endpoint: "/graphql"`,
`subscriptionEndpoint: "ws:///subscriptions"`,
}
respBody := resp.Body.String()

for _, e := range expectedBodyContains {
if !strings.Contains(respBody, e) {
t.Fatalf("wrong body, expected %s to contain %s", respBody, e)
}
}
}

func TestPlaygroundWithCustomConfig(t *testing.T) {
query := graphql.NewObject(graphql.ObjectConfig{
Name: "Query",
Fields: graphql.Fields{
"ping": &graphql.Field{
Name: "ping",
Type: graphql.String,
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
return "OK", nil
},
},
},
})

schema, err := graphql.NewSchema(graphql.SchemaConfig{
Query: query,
})
if err != nil {
t.Fatal(err)
}

req, err := http.NewRequest("GET", "/custom-path/graphql", nil)
req.Header.Set("Accept", "text/html")
if err != nil {
t.Fatal(err)
}

h := handler.New(&handler.Config{
Schema: &schema,
Playground: true,
PlaygroundConfig: &handler.PlaygroundConfig{
Endpoint: "/custom-path/graphql",
SubscriptionEndpoint: "/custom-path/ws",
},
})

resp := httptest.NewRecorder()
h.ContextHandler(context.Background(), resp, req)

if resp.Code != http.StatusOK {
t.Fatalf("unexpected server response %v", resp.Code)
}

expectedBodyContains := []string{
"GraphQL Playground",
`endpoint: "/custom-path/graphql"`,
`subscriptionEndpoint: "/custom-path/ws"`,
}
respBody := resp.Body.String()

for _, e := range expectedBodyContains {
if !strings.Contains(respBody, e) {
t.Fatalf("wrong body, expected %s to contain %s", respBody, e)
}
}
}

0 comments on commit 62f4eb5

Please sign in to comment.