1package handler
2
3import (
4 "encoding/json"
5 "html/template"
6 "net/http"
7
8 "github.com/graphql-go/graphql"
9)
10
11// page is the page data structure of the rendered GraphiQL page
12type graphiqlPage struct {
13 GraphiqlVersion string
14 QueryString string
15 ResultString string
16 VariablesString string
17 OperationName string
18}
19
20// renderGraphiQL renders the GraphiQL GUI
21func renderGraphiQL(w http.ResponseWriter, params graphql.Params) {
22 t := template.New("GraphiQL")
23 t, err := t.Parse(graphiqlTemplate)
24 if err != nil {
25 http.Error(w, err.Error(), http.StatusInternalServerError)
26 return
27 }
28
29 // Create variables string
30 vars, err := json.MarshalIndent(params.VariableValues, "", " ")
31 if err != nil {
32 http.Error(w, err.Error(), http.StatusInternalServerError)
33 return
34 }
35 varsString := string(vars)
36 if varsString == "null" {
37 varsString = ""
38 }
39
40 // Create result string
41 var resString string
42 if params.RequestString == "" {
43 resString = ""
44 } else {
45 result, err := json.MarshalIndent(graphql.Do(params), "", " ")
46 if err != nil {
47 http.Error(w, err.Error(), http.StatusInternalServerError)
48 return
49 }
50 resString = string(result)
51 }
52
53 p := graphiqlPage{
54 GraphiqlVersion: graphiqlVersion,
55 QueryString: params.RequestString,
56 ResultString: resString,
57 VariablesString: varsString,
58 OperationName: params.OperationName,
59 }
60
61 err = t.ExecuteTemplate(w, "index", p)
62 if err != nil {
63 http.Error(w, err.Error(), http.StatusInternalServerError)
64 }
65 return
66}
67
68// graphiqlVersion is the current version of GraphiQL
69const graphiqlVersion = "0.11.3"
70
71// tmpl is the page template to render GraphiQL
72const graphiqlTemplate = `
73{{ define "index" }}
74<!--
75The request to this GraphQL server provided the header "Accept: text/html"
76and as a result has been presented GraphiQL - an in-browser IDE for
77exploring GraphQL.
78
79If you wish to receive JSON, provide the header "Accept: application/json" or
80add "&raw" to the end of the URL within a browser.
81-->
82<!DOCTYPE html>
83<html>
84<head>
85 <meta charset="utf-8" />
86 <title>GraphiQL</title>
87 <meta name="robots" content="noindex" />
88 <style>
89 html, body {
90 height: 100%;
91 margin: 0;
92 overflow: hidden;
93 width: 100%;
94 }
95 </style>
96 <link href="//cdn.jsdelivr.net/npm/graphiql@{{ .GraphiqlVersion }}/graphiql.css" rel="stylesheet" />
97 <script src="//cdn.jsdelivr.net/fetch/0.9.0/fetch.min.js"></script>
98 <script src="//cdn.jsdelivr.net/react/15.4.2/react.min.js"></script>
99 <script src="//cdn.jsdelivr.net/react/15.4.2/react-dom.min.js"></script>
100 <script src="//cdn.jsdelivr.net/npm/graphiql@{{ .GraphiqlVersion }}/graphiql.min.js"></script>
101</head>
102<body>
103 <script>
104 // Collect the URL parameters
105 var parameters = {};
106 window.location.search.substr(1).split('&').forEach(function (entry) {
107 var eq = entry.indexOf('=');
108 if (eq >= 0) {
109 parameters[decodeURIComponent(entry.slice(0, eq))] =
110 decodeURIComponent(entry.slice(eq + 1));
111 }
112 });
113
114 // Produce a Location query string from a parameter object.
115 function locationQuery(params) {
116 return '?' + Object.keys(params).filter(function (key) {
117 return Boolean(params[key]);
118 }).map(function (key) {
119 return encodeURIComponent(key) + '=' +
120 encodeURIComponent(params[key]);
121 }).join('&');
122 }
123
124 // Derive a fetch URL from the current URL, sans the GraphQL parameters.
125 var graphqlParamNames = {
126 query: true,
127 variables: true,
128 operationName: true
129 };
130
131 var otherParams = {};
132 for (var k in parameters) {
133 if (parameters.hasOwnProperty(k) && graphqlParamNames[k] !== true) {
134 otherParams[k] = parameters[k];
135 }
136 }
137 var fetchURL = locationQuery(otherParams);
138
139 // Defines a GraphQL fetcher using the fetch API.
140 function graphQLFetcher(graphQLParams) {
141 return fetch(fetchURL, {
142 method: 'post',
143 headers: {
144 'Accept': 'application/json',
145 'Content-Type': 'application/json'
146 },
147 body: JSON.stringify(graphQLParams),
148 credentials: 'include',
149 }).then(function (response) {
150 return response.text();
151 }).then(function (responseBody) {
152 try {
153 return JSON.parse(responseBody);
154 } catch (error) {
155 return responseBody;
156 }
157 });
158 }
159
160 // When the query and variables string is edited, update the URL bar so
161 // that it can be easily shared.
162 function onEditQuery(newQuery) {
163 parameters.query = newQuery;
164 updateURL();
165 }
166
167 function onEditVariables(newVariables) {
168 parameters.variables = newVariables;
169 updateURL();
170 }
171
172 function onEditOperationName(newOperationName) {
173 parameters.operationName = newOperationName;
174 updateURL();
175 }
176
177 function updateURL() {
178 history.replaceState(null, null, locationQuery(parameters));
179 }
180
181 // Render <GraphiQL /> into the body.
182 ReactDOM.render(
183 React.createElement(GraphiQL, {
184 fetcher: graphQLFetcher,
185 onEditQuery: onEditQuery,
186 onEditVariables: onEditVariables,
187 onEditOperationName: onEditOperationName,
188 query: {{ .QueryString }},
189 response: {{ .ResultString }},
190 variables: {{ .VariablesString }},
191 operationName: {{ .OperationName }},
192 }),
193 document.body
194 );
195 </script>
196</body>
197</html>
198{{ end }}
199`