Spaces:
Running
Running
// _ _ | |
// __ _____ __ ___ ___ __ _| |_ ___ | |
// \ \ /\ / / _ \/ _` \ \ / / |/ _` | __/ _ \ | |
// \ V V / __/ (_| |\ V /| | (_| | || __/ | |
// \_/\_/ \___|\__,_| \_/ |_|\__,_|\__\___| | |
// | |
// Copyright © 2016 - 2024 Weaviate B.V. All rights reserved. | |
// | |
// CONTACT: [email protected] | |
// | |
// Based on `graphiql.go` from https://github.com/graphql-go/handler | |
// only made RenderGraphiQL a public function. | |
package graphiql | |
import ( | |
"encoding/json" | |
"html/template" | |
"net/http" | |
"strings" | |
) | |
// graphiqlVersion is the current version of GraphiQL | |
const graphiqlVersion = "0.11.11" | |
// graphiqlData is the page data structure of the rendered GraphiQL page | |
type graphiqlData struct { | |
GraphiqlVersion string | |
QueryString string | |
Variables string | |
OperationName string | |
AuthKey string | |
AuthToken string | |
} | |
func AddMiddleware(next http.Handler) http.Handler { | |
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | |
if strings.HasPrefix(r.URL.Path, "/v1/graphql") && r.Method == http.MethodGet { | |
renderGraphiQL(w, r) | |
} else { | |
next.ServeHTTP(w, r) | |
} | |
}) | |
} | |
// renderGraphiQL renders the GraphiQL GUI | |
func renderGraphiQL(w http.ResponseWriter, r *http.Request) { | |
w.Header().Set("WWW-Authenticate", `Basic realm="Provide your key and token (as username as password respectively)"`) | |
user, password, authOk := r.BasicAuth() | |
if !authOk { | |
http.Error(w, "Not authorized", 401) | |
return | |
} | |
queryParams := r.URL.Query() | |
t := template.New("GraphiQL") | |
t, err := t.Parse(graphiqlTemplate) | |
if err != nil { | |
http.Error(w, err.Error(), http.StatusInternalServerError) | |
return | |
} | |
// Attempt to deserialize the 'variables' query key to something reasonable. | |
var queryVars interface{} | |
err = json.Unmarshal([]byte(queryParams.Get("variables")), &queryVars) | |
var varsString string | |
if err == nil { | |
vars, err := json.MarshalIndent(queryVars, "", " ") | |
if err != nil { | |
http.Error(w, err.Error(), http.StatusInternalServerError) | |
return | |
} | |
varsString = string(vars) | |
if varsString == "null" { | |
varsString = "" | |
} | |
} | |
// Create result string | |
d := graphiqlData{ | |
GraphiqlVersion: graphiqlVersion, | |
QueryString: queryParams.Get("query"), | |
Variables: varsString, | |
OperationName: queryParams.Get("operationName"), | |
AuthKey: user, | |
AuthToken: password, | |
} | |
err = t.ExecuteTemplate(w, "index", d) | |
if err != nil { | |
http.Error(w, err.Error(), http.StatusInternalServerError) | |
} | |
} | |
// tmpl is the page template to render GraphiQL | |
const graphiqlTemplate = ` | |
{{ define "index" }} | |
<!-- | |
The request to this GraphQL server provided the header "Accept: text/html" | |
and as a result has been presented GraphiQL - an in-browser IDE for | |
exploring GraphQL. | |
If you wish to receive JSON, provide the header "Accept: application/json" or | |
add "&raw" to the end of the URL within a browser. | |
--> | |
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="utf-8" /> | |
<title>GraphiQL</title> | |
<meta name="robots" content="noindex" /> | |
<meta name="referrer" content="origin"> | |
<style> | |
body { | |
height: 100%; | |
margin: 0; | |
overflow: hidden; | |
width: 100%; | |
} | |
#graphiql { | |
height: 100vh; | |
} | |
</style> | |
<link href="//cdn.jsdelivr.net/npm/graphiql@{{ .GraphiqlVersion }}/graphiql.css" rel="stylesheet" /> | |
<script src="//cdn.jsdelivr.net/es6-promise/4.0.5/es6-promise.auto.min.js"></script> | |
<script src="//cdn.jsdelivr.net/fetch/0.9.0/fetch.min.js"></script> | |
<script src="//cdn.jsdelivr.net/react/15.4.2/react.min.js"></script> | |
<script src="//cdn.jsdelivr.net/react/15.4.2/react-dom.min.js"></script> | |
<script src="//cdn.jsdelivr.net/npm/graphiql@{{ .GraphiqlVersion }}/graphiql.min.js"></script> | |
</head> | |
<body> | |
<div id="graphiql">Loading...</div> | |
<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) { | |
return '?' + Object.keys(params).filter(function (key) { | |
return Boolean(params[key]); | |
}).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]; | |
} | |
} | |
var fetchURL = locationQuery(otherParams); | |
// Defines a GraphQL fetcher using the fetch API. | |
function graphQLFetcher(graphQLParams) { | |
return fetch(fetchURL, { | |
method: 'post', | |
headers: { | |
'Accept': 'application/json', | |
'Content-Type': 'application/json', | |
'X-API-KEY': {{ .AuthKey }}, | |
'X-API-TOKEN': {{ .AuthToken }} | |
}, | |
body: JSON.stringify(graphQLParams), | |
credentials: 'include', | |
}).then(function (response) { | |
return response.text(); | |
}).then(function (responseBody) { | |
try { | |
return JSON.parse(responseBody); | |
} catch (error) { | |
return responseBody; | |
} | |
}); | |
} | |
// 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; | |
updateURL(); | |
} | |
function onEditVariables(newVariables) { | |
parameters.variables = newVariables; | |
updateURL(); | |
} | |
function onEditOperationName(newOperationName) { | |
parameters.operationName = newOperationName; | |
updateURL(); | |
} | |
function updateURL() { | |
history.replaceState(null, null, locationQuery(parameters)); | |
} | |
// Render <GraphiQL /> into the body. | |
ReactDOM.render( | |
React.createElement(GraphiQL, { | |
fetcher: graphQLFetcher, | |
onEditQuery: onEditQuery, | |
onEditVariables: onEditVariables, | |
onEditOperationName: onEditOperationName, | |
query: {{ .QueryString }}, | |
response: null, | |
variables: {{ .Variables }}, | |
operationName: {{ .OperationName }}, | |
}), | |
document.getElementById('graphiql') | |
); | |
</script> | |
</body> | |
</html> | |
{{ end }} | |
` | |