Skip to content

Commit 84c7c23

Browse files
authored
Merge pull request #37 from sinwailam193/add-graphcool-playground
added playground
2 parents c1c8ee7 + 8de16ad commit 84c7c23

File tree

4 files changed

+235
-12
lines changed

4 files changed

+235
-12
lines changed

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,16 @@ func main() {
3333
}
3434
```
3535

36+
### Using Playground
37+
```go
38+
h := handler.New(&handler.Config{
39+
Schema: &schema,
40+
Pretty: true,
41+
GraphiQL: false,
42+
Playground: true,
43+
})
44+
```
45+
3646
### Details
3747

3848
The handler will accept requests with

graphcoolPlayground.go

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
package handler
2+
3+
import (
4+
"fmt"
5+
"html/template"
6+
"net/http"
7+
)
8+
9+
type playgroundData struct {
10+
PlaygroundVersion string
11+
Endpoint string
12+
SubscriptionEndpoint string
13+
SetTitle bool
14+
}
15+
16+
// renderPlayground renders the Playground GUI
17+
func renderPlayground(w http.ResponseWriter, r *http.Request) {
18+
t := template.New("Playground")
19+
t, err := t.Parse(graphcoolPlaygroundTemplate)
20+
if err != nil {
21+
http.Error(w, err.Error(), http.StatusInternalServerError)
22+
return
23+
}
24+
25+
d := playgroundData{
26+
PlaygroundVersion: graphcoolPlaygroundVersion,
27+
Endpoint: "/graphql",
28+
SubscriptionEndpoint: fmt.Sprintf("ws://%v/subscriptions", r.Host),
29+
SetTitle: true,
30+
}
31+
err = t.ExecuteTemplate(w, "index", d)
32+
if err != nil {
33+
http.Error(w, err.Error(), http.StatusInternalServerError)
34+
}
35+
36+
return
37+
}
38+
39+
const graphcoolPlaygroundVersion = "1.5.2"
40+
41+
const graphcoolPlaygroundTemplate = `
42+
{{ define "index" }}
43+
<!--
44+
The request to this GraphQL server provided the header "Accept: text/html"
45+
and as a result has been presented Playground - an in-browser IDE for
46+
exploring GraphQL.
47+
48+
If you wish to receive JSON, provide the header "Accept: application/json" or
49+
add "&raw" to the end of the URL within a browser.
50+
-->
51+
<!DOCTYPE html>
52+
<html>
53+
54+
<head>
55+
<meta charset=utf-8/>
56+
<meta name="viewport" content="user-scalable=no, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, minimal-ui">
57+
<title>GraphQL Playground</title>
58+
<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/graphql-playground-react/build/static/css/index.css" />
59+
<link rel="shortcut icon" href="//cdn.jsdelivr.net/npm/graphql-playground-react/build/favicon.png" />
60+
<script src="//cdn.jsdelivr.net/npm/graphql-playground-react/build/static/js/middleware.js"></script>
61+
</head>
62+
63+
<body>
64+
<div id="root">
65+
<style>
66+
body {
67+
background-color: rgb(23, 42, 58);
68+
font-family: Open Sans, sans-serif;
69+
height: 90vh;
70+
}
71+
#root {
72+
height: 100%;
73+
width: 100%;
74+
display: flex;
75+
align-items: center;
76+
justify-content: center;
77+
}
78+
.loading {
79+
font-size: 32px;
80+
font-weight: 200;
81+
color: rgba(255, 255, 255, .6);
82+
margin-left: 20px;
83+
}
84+
img {
85+
width: 78px;
86+
height: 78px;
87+
}
88+
.title {
89+
font-weight: 400;
90+
}
91+
</style>
92+
<img src='//cdn.jsdelivr.net/npm/graphql-playground-react/build/logo.png' alt=''>
93+
<div class="loading"> Loading
94+
<span class="title">GraphQL Playground</span>
95+
</div>
96+
</div>
97+
<script>window.addEventListener('load', function (event) {
98+
GraphQLPlayground.init(document.getElementById('root'), {
99+
// options as 'endpoint' belong here
100+
endpoint: {{ .Endpoint }},
101+
subscriptionEndpoint: {{ .SubscriptionEndpoint }},
102+
setTitle: {{ .SetTitle }}
103+
})
104+
})</script>
105+
</body>
106+
107+
</html>
108+
{{ end }}
109+
`

graphcoolPlayground_test.go

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
package handler_test
2+
3+
import (
4+
"net/http"
5+
"net/http/httptest"
6+
"strings"
7+
"testing"
8+
9+
"github.com/graphql-go/graphql/testutil"
10+
"github.com/graphql-go/handler"
11+
)
12+
13+
func TestRenderPlayground(t *testing.T) {
14+
cases := map[string]struct {
15+
playgroundEnabled bool
16+
accept string
17+
url string
18+
expectedStatusCode int
19+
expectedContentType string
20+
expectedBodyContains string
21+
}{
22+
"renders Playground": {
23+
playgroundEnabled: true,
24+
accept: "text/html",
25+
expectedStatusCode: http.StatusOK,
26+
expectedContentType: "text/html; charset=utf-8",
27+
expectedBodyContains: "<!DOCTYPE html>",
28+
},
29+
"doesn't render Playground if turned off": {
30+
playgroundEnabled: false,
31+
accept: "text/html",
32+
expectedStatusCode: http.StatusOK,
33+
expectedContentType: "application/json; charset=utf-8",
34+
},
35+
"doesn't render Playground if Content-Type application/json is present": {
36+
playgroundEnabled: true,
37+
accept: "application/json,text/html",
38+
expectedStatusCode: http.StatusOK,
39+
expectedContentType: "application/json; charset=utf-8",
40+
},
41+
"doesn't render Playground if Content-Type text/html is not present": {
42+
playgroundEnabled: true,
43+
expectedStatusCode: http.StatusOK,
44+
expectedContentType: "application/json; charset=utf-8",
45+
},
46+
"doesn't render Playground if 'raw' query is present": {
47+
playgroundEnabled: true,
48+
accept: "text/html",
49+
url: "?raw",
50+
expectedStatusCode: http.StatusOK,
51+
expectedContentType: "application/json; charset=utf-8",
52+
},
53+
}
54+
55+
for tcID, tc := range cases {
56+
t.Run(tcID, func(t *testing.T) {
57+
req, err := http.NewRequest(http.MethodGet, tc.url, nil)
58+
if err != nil {
59+
t.Error(err)
60+
}
61+
62+
req.Header.Set("Accept", tc.accept)
63+
64+
h := handler.New(&handler.Config{
65+
Schema: &testutil.StarWarsSchema,
66+
GraphiQL: false,
67+
Playground: tc.playgroundEnabled,
68+
})
69+
70+
rr := httptest.NewRecorder()
71+
72+
h.ServeHTTP(rr, req)
73+
resp := rr.Result()
74+
75+
statusCode := resp.StatusCode
76+
if statusCode != tc.expectedStatusCode {
77+
t.Fatalf("%s: wrong status code, expected %v, got %v", tcID, tc.expectedStatusCode, statusCode)
78+
}
79+
80+
contentType := resp.Header.Get("Content-Type")
81+
if contentType != tc.expectedContentType {
82+
t.Fatalf("%s: wrong content type, expected %s, got %s", tcID, tc.expectedContentType, contentType)
83+
}
84+
85+
body := rr.Body.String()
86+
if !strings.Contains(body, tc.expectedBodyContains) {
87+
t.Fatalf("%s: wrong body, expected %s to contain %s", tcID, body, tc.expectedBodyContains)
88+
}
89+
})
90+
}
91+
}

handler.go

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,10 @@ const (
1919
)
2020

2121
type Handler struct {
22-
Schema *graphql.Schema
23-
pretty bool
24-
graphiql bool
22+
Schema *graphql.Schema
23+
pretty bool
24+
graphiql bool
25+
playground bool
2526
}
2627
type RequestOptions struct {
2728
Query string `json:"query" url:"query" schema:"query"`
@@ -138,6 +139,15 @@ func (h *Handler) ContextHandler(ctx context.Context, w http.ResponseWriter, r *
138139
}
139140
}
140141

142+
if h.playground {
143+
acceptHeader := r.Header.Get("Accept")
144+
_, raw := r.URL.Query()["raw"]
145+
if !raw && !strings.Contains(acceptHeader, "application/json") && strings.Contains(acceptHeader, "text/html") {
146+
renderPlayground(w, r)
147+
return
148+
}
149+
}
150+
141151
// use proper JSON Header
142152
w.Header().Add("Content-Type", "application/json; charset=utf-8")
143153

@@ -160,16 +170,18 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
160170
}
161171

162172
type Config struct {
163-
Schema *graphql.Schema
164-
Pretty bool
165-
GraphiQL bool
173+
Schema *graphql.Schema
174+
Pretty bool
175+
GraphiQL bool
176+
Playground bool
166177
}
167178

168179
func NewConfig() *Config {
169180
return &Config{
170-
Schema: nil,
171-
Pretty: true,
172-
GraphiQL: true,
181+
Schema: nil,
182+
Pretty: true,
183+
GraphiQL: true,
184+
Playground: false,
173185
}
174186
}
175187

@@ -182,8 +194,9 @@ func New(p *Config) *Handler {
182194
}
183195

184196
return &Handler{
185-
Schema: p.Schema,
186-
pretty: p.Pretty,
187-
graphiql: p.GraphiQL,
197+
Schema: p.Schema,
198+
pretty: p.Pretty,
199+
graphiql: p.GraphiQL,
200+
playground: p.Playground,
188201
}
189202
}

0 commit comments

Comments
 (0)