55 */
66package io .jooby .graphql ;
77
8+ import javax .annotation .Nonnull ;
9+
810import io .jooby .Extension ;
911import io .jooby .Jooby ;
1012import io .jooby .MediaType ;
1113
12- import javax .annotation .Nonnull ;
13-
1414/**
1515 * GraphiQL module: https://github.com/graphql/graphiql.
1616 *
3131 */
3232public class GraphiQLModule implements Extension {
3333
34- private static final String RESOURCES =
35- " <link href=\" {{contextPath}}/graphql/static/graphiql.css\" rel=\" stylesheet\" />\n "
36- + " <script src=\" {{contextPath}}/graphql/static/es6-promise.auto.min.js\" ></script>\n "
37- + " <script src=\" {{contextPath}}/graphql/static/fetch.min.js\" ></script>\n "
38- + " <script src=\" {{contextPath}}/graphql/static/react.min.js\" ></script>\n "
39- + " <script src=\" {{contextPath}}/graphql/static/react-dom.min.js\" ></script>\n "
40- + " <script src=\" {{contextPath}}/graphql/static/graphiql.min.js\" ></script>\n " ;
34+ private static final String RESOURCES_CSS =
35+ " <link href=\" {{contextPath}}/graphql/static/graphiql.css\" rel=\" stylesheet\" />\n " ;
36+
37+ private static final String RESOURCES_JS = " <script src=\" {{contextPath}}/graphql/static/graphiql.min.js\" ></script>\n " ;
4138
4239 @ Override public void install (@ Nonnull Jooby application ) throws Exception {
4340 String cpath = application .getContextPath ();
@@ -51,122 +48,245 @@ public class GraphiQLModule implements Extension {
5148 application .get (graphqlPath , ctx -> ctx .setResponseType (MediaType .html ).send (index ));
5249 }
5350
54- private static final String INDEX = "<!--\n "
55- + "The request to this GraphQL server provided the header \" Accept: text/html\" \n "
56- + "and as a result has been presented GraphiQL - an in-browser IDE for\n "
57- + "exploring GraphQL.\n "
58- + "\n "
59- + "If you wish to receive JSON, provide the header \" Accept: application/json\" or\n "
60- + "add \" &raw\" to the end of the URL within a browser.\n "
61- + "-->\n "
51+ private static final String INDEX = "\n "
6252 + "<!DOCTYPE html>\n "
63- + "<html>\n "
53+ + "<html lang= \" en \" >\n "
6454 + "<head>\n "
6555 + " <meta charset=\" utf-8\" />\n "
66- + " <title>GraphiQL</title>\n "
6756 + " <meta name=\" robots\" content=\" noindex\" />\n "
6857 + " <meta name=\" referrer\" content=\" origin\" />\n "
6958 + " <meta name=\" viewport\" content=\" width=device-width, initial-scale=1\" />\n "
59+ + " <title>SWAPI GraphQL API</title>\n "
7060 + " <style>\n "
7161 + " body {\n "
62+ + " height: 100vh;\n "
7263 + " margin: 0;\n "
7364 + " overflow: hidden;\n "
7465 + " }\n "
75- + " #graphiql {\n "
66+ + " #splash {\n "
67+ + " color: #333;\n "
68+ + " display: flex;\n "
69+ + " flex-direction: column;\n "
70+ + " font-family: system, -apple-system, \" San Francisco\" , \" .SFNSDisplay-Regular\" , \" Segoe UI\" , Segoe, \" Segoe WP\" , \" Helvetica Neue\" , helvetica, \" Lucida Grande\" , arial, sans-serif;\n "
7671 + " height: 100vh;\n "
72+ + " justify-content: center;\n "
73+ + " text-align: center;\n "
7774 + " }\n "
7875 + " </style>\n "
79- + RESOURCES
76+ + " <link rel=\" icon\" href=\" favicon.ico\" >\n "
77+ + RESOURCES_CSS
8078 + "</head>\n "
8179 + "<body>\n "
82- + "<div id=\" graphiql\" >Loading...</div>\n "
83- + "<script>\n "
84- + " // Collect the URL parameters\n "
85- + " var parameters = {};\n "
86- + " window.location.search.substr(1).split('&').forEach(function (entry) {\n "
87- + " var eq = entry.indexOf('=');\n "
88- + " if (eq >= 0) {\n "
89- + " parameters[decodeURIComponent(entry.slice(0, eq))] =\n "
90- + " decodeURIComponent(entry.slice(eq + 1));\n "
91- + " }\n "
92- + " });\n "
93- + "\n "
94- + " // Produce a Location query string from a parameter object.\n "
95- + " function locationQuery(params) {\n "
96- + " return '?' + Object.keys(params).filter(function (key) {\n "
97- + " return Boolean(params[key]);\n "
98- + " }).map(function (key) {\n "
99- + " return encodeURIComponent(key) + '=' +\n "
100- + " encodeURIComponent(params[key]);\n "
101- + " }).join('&');\n "
102- + " }\n "
80+ + " <div id=\" splash\" >\n "
81+ + " Loading…\n "
82+ + " </div>\n "
83+ + " <script src=\" //cdn.jsdelivr.net/es6-promise/4.0.5/es6-promise.auto.min.js\" ></script>\n "
84+ + " <script src=\" https://cdn.jsdelivr.net/npm/react/umd/react.production.min.js\" ></script>\n "
85+ + " <script src=\" https://cdn.jsdelivr.net/npm/react-dom/umd/react-dom.production.min.js\" ></script>\n "
86+ + RESOURCES_JS
87+ + " <script>\n "
88+ + " // Parse the search string to get url parameters.\n "
89+ + " var search = window.location.search;\n "
90+ + " var parameters = {};\n "
91+ + " search.substr(1).split('&').forEach(function (entry) {\n "
92+ + " var eq = entry.indexOf('=');\n "
93+ + " if (eq >= 0) {\n "
94+ + " parameters[decodeURIComponent(entry.slice(0, eq))] =\n "
95+ + " decodeURIComponent(entry.slice(eq + 1));\n "
96+ + " }\n "
97+ + " });\n "
10398 + "\n "
104- + " // Derive a fetch URL from the current URL, sans the GraphQL parameters.\n "
105- + " var graphqlParamNames = {\n "
106- + " query: true,\n "
107- + " variables: true,\n "
108- + " operationName: true\n "
109- + " };\n "
99+ + " // if variables was provided, try to format it.\n "
100+ + " if (parameters.variables) {\n "
101+ + " try {\n "
102+ + " parameters.variables =\n "
103+ + " JSON.stringify(JSON.parse(parameters.variables), null, 2);\n "
104+ + " } catch (e) {\n "
105+ + " // Do nothing, we want to display the invalid JSON as a string, rather\n "
106+ + " // than present an error.\n "
107+ + " }\n "
108+ + " }\n "
110109 + "\n "
111- + " var otherParams = {};\n "
112- + " for (var k in parameters) {\n "
113- + " if (parameters.hasOwnProperty(k) && graphqlParamNames[k] !== true) {\n "
114- + " otherParams[k] = parameters[k];\n "
115- + " }\n "
116- + " }\n "
117- + " var fetchURL = locationQuery(otherParams);\n "
110+ + " // When the query and variables string is edited, update the URL bar so\n "
111+ + " // that it can be easily shared\n "
112+ + " function onEditQuery(newQuery) {\n "
113+ + " parameters.query = newQuery;\n "
114+ + " updateURL();\n "
115+ + " }\n "
116+ + " function onEditVariables(newVariables) {\n "
117+ + " parameters.variables = newVariables;\n "
118+ + " updateURL();\n "
119+ + " }\n "
120+ + " function onEditOperationName(newOperationName) {\n "
121+ + " parameters.operationName = newOperationName;\n "
122+ + " updateURL();\n "
123+ + " }\n "
124+ + " function updateURL() {\n "
125+ + " var newSearch = '?' + Object.keys(parameters).filter(function (key) {\n "
126+ + " return Boolean(parameters[key]);\n "
127+ + " }).map(function (key) {\n "
128+ + " return encodeURIComponent(key) + '=' +\n "
129+ + " encodeURIComponent(parameters[key]);\n "
130+ + " }).join('&');\n "
131+ + " history.replaceState(null, null, newSearch);\n "
132+ + " }\n "
118133 + "\n "
119- + " // Defines a GraphQL fetcher using the fetch API.\n "
120- + " function graphQLFetcher(graphQLParams) {\n "
121- + " return fetch(fetchURL, {\n "
122- + " method: 'post',\n "
123- + " headers: {\n "
124- + " 'Accept': 'application/json',\n "
125- + " 'Content-Type': 'application/json'\n "
126- + " },\n "
127- + " body: JSON.stringify(graphQLParams),\n "
128- + " credentials: 'include',\n "
129- + " }).then(function (response) {\n "
130- + " return response.json();\n "
131- + " });\n "
132- + " }\n "
134+ + " function graphQLFetcher(graphQLParams) {\n "
135+ + " // This example expects a GraphQL server at the path /graphql.\n "
136+ + " // Change this to point wherever you host your GraphQL server.\n "
137+ + " return fetch(parameters.fetchURL || 'https://swapi-graphql.netlify.app/.netlify/functions/index', {\n "
138+ + " method: 'post',\n "
139+ + " headers: {\n "
140+ + " 'Accept': 'application/json',\n "
141+ + " 'Content-Type': 'application/json'\n "
142+ + " },\n "
143+ + " body: JSON.stringify(graphQLParams),\n "
144+ + " }).then(function (response) {\n "
145+ + " return response.text();\n "
146+ + " }).then(function (responseBody) {\n "
147+ + " try {\n "
148+ + " return JSON.parse(responseBody);\n "
149+ + " } catch (error) {\n "
150+ + " return responseBody;\n "
151+ + " }\n "
152+ + " });\n "
153+ + " }\n "
133154 + "\n "
134- + " // When the query and variables string is edited, update the URL bar so\n "
135- + " // that it can be easily shared.\n "
136- + " function onEditQuery(newQuery) {\n "
137- + " parameters.query = newQuery;\n "
138- + " updateURL();\n "
139- + " }\n "
155+ + " // Render <GraphiQL /> into the body.\n "
156+ + " ReactDOM.render(\n "
157+ + " React.createElement(GraphiQL, {\n "
158+ + " fetcher: graphQLFetcher,\n "
159+ + " query: parameters.query,\n "
160+ + " variables: parameters.variables,\n "
161+ + " operationName: parameters.operationName,\n "
162+ + " onEditQuery: onEditQuery,\n "
163+ + " onEditVariables: onEditVariables,\n "
164+ + " onEditOperationName: onEditOperationName\n "
165+ + " }),\n "
166+ + " document.body,\n "
167+ + " );\n "
168+ + " </script>\n "
169+ + "</body>\n "
170+ + "</html>\n "
171+ + "\n \n "
172+ + "<!DOCTYPE html>\n "
173+ + "<html lang=\" en\" >\n "
174+ + "<head>\n "
175+ + " <meta charset=\" utf-8\" />\n "
176+ + " <meta name=\" robots\" content=\" noindex\" />\n "
177+ + " <meta name=\" referrer\" content=\" origin\" />\n "
178+ + " <meta name=\" viewport\" content=\" width=device-width, initial-scale=1\" />\n "
179+ + " <title>SWAPI GraphQL API</title>\n "
180+ + " <style>\n "
181+ + " body {\n "
182+ + " height: 100vh;\n "
183+ + " margin: 0;\n "
184+ + " overflow: hidden;\n "
185+ + " }\n "
186+ + " #splash {\n "
187+ + " color: #333;\n "
188+ + " display: flex;\n "
189+ + " flex-direction: column;\n "
190+ + " font-family: system, -apple-system, \" San Francisco\" , \" .SFNSDisplay-Regular\" , \" Segoe UI\" , Segoe, \" Segoe WP\" , \" Helvetica Neue\" , helvetica, \" Lucida Grande\" , arial, sans-serif;\n "
191+ + " height: 100vh;\n "
192+ + " justify-content: center;\n "
193+ + " text-align: center;\n "
194+ + " }\n "
195+ + " </style>\n "
196+ + " <link rel=\" icon\" href=\" favicon.ico\" >\n "
197+ + " <link type=\" text/css\" href=\" //unpkg.com/graphiql/graphiql.min.css\" rel=\" stylesheet\" />\n "
198+ + "</head>\n "
199+ + "<body>\n "
200+ + " <div id=\" splash\" >\n "
201+ + " Loading…\n "
202+ + " </div>\n "
203+ + " <script src=\" //cdn.jsdelivr.net/es6-promise/4.0.5/es6-promise.auto.min.js\" ></script>\n "
204+ + " <script src=\" https://cdn.jsdelivr.net/npm/react/umd/react.production.min.js\" ></script>\n "
205+ + " <script src=\" https://cdn.jsdelivr.net/npm/react-dom/umd/react-dom.production.min.js\" ></script>\n "
206+ + " <script src=\" //unpkg.com/graphiql/graphiql.min.js\" ></script>\n "
207+ + " <script>\n "
208+ + " // Parse the search string to get url parameters.\n "
209+ + " var search = window.location.search;\n "
210+ + " var parameters = {};\n "
211+ + " search.substr(1).split('&').forEach(function (entry) {\n "
212+ + " var eq = entry.indexOf('=');\n "
213+ + " if (eq >= 0) {\n "
214+ + " parameters[decodeURIComponent(entry.slice(0, eq))] =\n "
215+ + " decodeURIComponent(entry.slice(eq + 1));\n "
216+ + " }\n "
217+ + " });\n "
140218 + "\n "
141- + " function onEditVariables(newVariables) {\n "
142- + " parameters.variables = newVariables;\n "
143- + " updateURL();\n "
144- + " }\n "
219+ + " // if variables was provided, try to format it.\n "
220+ + " if (parameters.variables) {\n "
221+ + " try {\n "
222+ + " parameters.variables =\n "
223+ + " JSON.stringify(JSON.parse(parameters.variables), null, 2);\n "
224+ + " } catch (e) {\n "
225+ + " // Do nothing, we want to display the invalid JSON as a string, rather\n "
226+ + " // than present an error.\n "
227+ + " }\n "
228+ + " }\n "
145229 + "\n "
146- + " function onEditOperationName(newOperationName) {\n "
147- + " parameters.operationName = newOperationName;\n "
148- + " updateURL();\n "
149- + " }\n "
230+ + " // When the query and variables string is edited, update the URL bar so\n "
231+ + " // that it can be easily shared\n "
232+ + " function onEditQuery(newQuery) {\n "
233+ + " parameters.query = newQuery;\n "
234+ + " updateURL();\n "
235+ + " }\n "
236+ + " function onEditVariables(newVariables) {\n "
237+ + " parameters.variables = newVariables;\n "
238+ + " updateURL();\n "
239+ + " }\n "
240+ + " function onEditOperationName(newOperationName) {\n "
241+ + " parameters.operationName = newOperationName;\n "
242+ + " updateURL();\n "
243+ + " }\n "
244+ + " function updateURL() {\n "
245+ + " var newSearch = '?' + Object.keys(parameters).filter(function (key) {\n "
246+ + " return Boolean(parameters[key]);\n "
247+ + " }).map(function (key) {\n "
248+ + " return encodeURIComponent(key) + '=' +\n "
249+ + " encodeURIComponent(parameters[key]);\n "
250+ + " }).join('&');\n "
251+ + " history.replaceState(null, null, newSearch);\n "
252+ + " }\n "
150253 + "\n "
151- + " function updateURL() {\n "
152- + " history.replaceState(null, null, locationQuery(parameters));\n "
153- + " }\n "
254+ + " function graphQLFetcher(graphQLParams) {\n "
255+ + " // This example expects a GraphQL server at the path /graphql.\n "
256+ + " // Change this to point wherever you host your GraphQL server.\n "
257+ + " return fetch(parameters.fetchURL || 'https://swapi-graphql.netlify.app/.netlify/functions/index', {\n "
258+ + " method: 'post',\n "
259+ + " headers: {\n "
260+ + " 'Accept': 'application/json',\n "
261+ + " 'Content-Type': 'application/json'\n "
262+ + " },\n "
263+ + " body: JSON.stringify(graphQLParams),\n "
264+ + " }).then(function (response) {\n "
265+ + " return response.text();\n "
266+ + " }).then(function (responseBody) {\n "
267+ + " try {\n "
268+ + " return JSON.parse(responseBody);\n "
269+ + " } catch (error) {\n "
270+ + " return responseBody;\n "
271+ + " }\n "
272+ + " });\n "
273+ + " }\n "
154274 + "\n "
155- + " // Render <GraphiQL /> into the body.\n "
156- + " ReactDOM.render(\n "
157- + " React.createElement(GraphiQL, {\n "
158- + " fetcher: graphQLFetcher,\n "
159- + " onEditQuery: onEditQuery,\n "
160- + " onEditVariables: onEditVariables,\n "
161- + " onEditOperationName: onEditOperationName,\n "
162- + " query: undefined,\n "
163- + " response: undefined,\n "
164- + " variables: undefined,\n "
165- + " operationName: undefined,\n "
166- + " }),\n "
167- + " document.getElementById('graphiql')\n "
168- + " );\n "
169- + "</script>\n "
275+ + " // Render <GraphiQL /> into the body.\n "
276+ + " ReactDOM.render(\n "
277+ + " React.createElement(GraphiQL, {\n "
278+ + " fetcher: graphQLFetcher,\n "
279+ + " query: parameters.query,\n "
280+ + " variables: parameters.variables,\n "
281+ + " operationName: parameters.operationName,\n "
282+ + " onEditQuery: onEditQuery,\n "
283+ + " onEditVariables: onEditVariables,\n "
284+ + " onEditOperationName: onEditOperationName\n "
285+ + " }),\n "
286+ + " document.body,\n "
287+ + " );\n "
288+ + " </script>\n "
170289 + "</body>\n "
171- + "</html>\n " ;
290+ + "</html>\n "
291+ + "\n " ;
172292}
0 commit comments