UNPKG

apollo-server-module-graphiql

Version:
244 lines (227 loc) 7.89 kB
/* * Mostly taken straight from express-graphql, so see their licence * (https://github.com/graphql/express-graphql/blob/master/LICENSE) */ // TODO: in the future, build the GraphiQL app on the server, so it does not // depend on any CDN and can be run offline. /* * Arguments: * * - endpointURL: the relative or absolute URL for the endpoint which GraphiQL will make queries to * - (optional) query: the GraphQL query to pre-fill in the GraphiQL UI * - (optional) variables: a JS object of variables to pre-fill in the GraphiQL UI * - (optional) operationName: the operationName to pre-fill in the GraphiQL UI * - (optional) result: the result of the query to pre-fill in the GraphiQL UI * - (optional) passHeader: a string that will be added to the header object. * For example "'Authorization': localStorage['Meteor.loginToken']" for meteor * - (optional) editorTheme: a CodeMirror theme to be applied to the GraphiQL UI * - (optional) websocketConnectionParams: an object to pass to the web socket server */ export type GraphiQLData = { endpointURL: string; subscriptionsEndpoint?: string; query?: string; variables?: Object; operationName?: string; result?: Object; passHeader?: string; editorTheme?: string; websocketConnectionParams?: Object; rewriteURL?: boolean; }; // Current latest version of GraphiQL. const GRAPHIQL_VERSION = '0.11.11'; const SUBSCRIPTIONS_TRANSPORT_VERSION = '0.9.9'; // Ensures string values are safe to be used within a <script> tag. // TODO: I don't think that's the right escape function function safeSerialize(data) { return data ? JSON.stringify(data).replace(/\//g, '\\/') : null; } export function renderGraphiQL(data: GraphiQLData): string { const endpointURL = data.endpointURL; const endpointWs = endpointURL.startsWith('ws://') || endpointURL.startsWith('wss://'); const subscriptionsEndpoint = data.subscriptionsEndpoint; const usingHttp = !endpointWs; const usingWs = endpointWs || !!subscriptionsEndpoint; const endpointURLWs = usingWs && (endpointWs ? endpointURL : subscriptionsEndpoint); const queryString = data.query; const variablesString = data.variables ? JSON.stringify(data.variables, null, 2) : null; const resultString = null; const operationName = data.operationName; const passHeader = data.passHeader ? data.passHeader : ''; const editorTheme = data.editorTheme; const usingEditorTheme = !!editorTheme; const websocketConnectionParams = data.websocketConnectionParams || null; const rewriteURL = !!data.rewriteURL; /* eslint-disable max-len */ return ` <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>GraphiQL</title> <meta name="robots" content="noindex" /> <style> html, body { height: 100%; margin: 0; overflow: hidden; width: 100%; } </style> <link href="//unpkg.com/graphiql@${GRAPHIQL_VERSION}/graphiql.css" rel="stylesheet" /> <script src="//unpkg.com/react@15.6.1/dist/react.min.js"></script> <script src="//unpkg.com/react-dom@15.6.1/dist/react-dom.min.js"></script> <script src="//unpkg.com/graphiql@${GRAPHIQL_VERSION}/graphiql.min.js"></script> ${ usingEditorTheme ? `<link href="//cdn.jsdelivr.net/npm/codemirror@5/theme/${editorTheme}.min.css" rel="stylesheet" />` : '' } ${ usingHttp ? `<script src="//cdn.jsdelivr.net/fetch/2.0.1/fetch.min.js"></script>` : '' } ${ usingWs ? `<script src="//unpkg.com/subscriptions-transport-ws@${SUBSCRIPTIONS_TRANSPORT_VERSION}/browser/client.js"></script>` : '' } ${ usingWs && usingHttp ? '<script src="//unpkg.com/graphiql-subscriptions-fetcher@0.0.2/browser/client.js"></script>' : '' } </head> <body> <script> // Collect the URL parameters var parameters = {}; window.location.search.substr(1).split('&').forEach(function (entry) { var eq = entry.indexOf('='); if (eq >= 0) { parameters[decodeURIComponent(entry.slice(0, eq))] = decodeURIComponent(entry.slice(eq + 1)); } }); // Produce a Location query string from a parameter object. function locationQuery(params, location) { return (location ? location: '') + '?' + Object.keys(params).map(function (key) { return encodeURIComponent(key) + '=' + encodeURIComponent(params[key]); }).join('&'); } // Derive a fetch URL from the current URL, sans the GraphQL parameters. var graphqlParamNames = { query: true, variables: true, operationName: true }; var otherParams = {}; for (var k in parameters) { if (parameters.hasOwnProperty(k) && graphqlParamNames[k] !== true) { otherParams[k] = parameters[k]; } } ${ usingWs ? ` var subscriptionsClient = new window.SubscriptionsTransportWs.SubscriptionClient('${endpointURLWs}', { reconnect: true${ websocketConnectionParams ? `, connectionParams: ${JSON.stringify(websocketConnectionParams)}` : '' } }); var graphQLWSFetcher = subscriptionsClient.request.bind(subscriptionsClient); ` : '' } ${ usingHttp ? ` // We don't use safe-serialize for location, because it's not client input. var fetchURL = locationQuery(otherParams, '${endpointURL}'); // Defines a GraphQL fetcher using the fetch API. function graphQLHttpFetcher(graphQLParams) { return fetch(fetchURL, { method: 'post', headers: { 'Accept': 'application/json', 'Content-Type': 'application/json', ${passHeader} }, body: JSON.stringify(graphQLParams), credentials: 'same-origin', }).then(function (response) { return response.text(); }).then(function (responseBody) { try { return JSON.parse(responseBody); } catch (error) { return responseBody; } }); } ` : '' } ${ usingWs && usingHttp ? ` var fetcher = window.GraphiQLSubscriptionsFetcher.graphQLFetcher(subscriptionsClient, graphQLHttpFetcher); ` : ` var fetcher = ${usingWs ? 'graphQLWSFetcher' : 'graphQLHttpFetcher'}; ` } // When the query and variables string is edited, update the URL bar so // that it can be easily shared. function onEditQuery(newQuery) { parameters.query = newQuery; ${rewriteURL ? 'updateURL();' : ''} } function onEditVariables(newVariables) { parameters.variables = newVariables; ${rewriteURL ? 'updateURL();' : ''} } function onEditOperationName(newOperationName) { parameters.operationName = newOperationName; ${rewriteURL ? 'updateURL();' : ''} } function updateURL() { var cleanParams = Object.keys(parameters).filter(function(v) { return parameters[v]; }).reduce(function(old, v) { old[v] = parameters[v]; return old; }, {}); history.replaceState(null, null, locationQuery(cleanParams) + window.location.hash); } // Render <GraphiQL /> into the body. ReactDOM.render( React.createElement(GraphiQL, { fetcher: fetcher, onEditQuery: onEditQuery, onEditVariables: onEditVariables, onEditOperationName: onEditOperationName, query: ${safeSerialize(queryString)}, response: ${safeSerialize(resultString)}, variables: ${safeSerialize(variablesString)}, operationName: ${safeSerialize(operationName)}, editorTheme: ${safeSerialize(editorTheme)}, websocketConnectionParams: ${safeSerialize(websocketConnectionParams)}, }), document.body ); </script> </body> </html>`; }