Skip to content

Allow custom endpoint URL. #33

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
131 changes: 93 additions & 38 deletions graphiql.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,27 @@ import (
"encoding/json"
"html/template"
"net/http"
"strings"

"github.com/graphql-go/graphql"
)

// page is the page data structure of the rendered GraphiQL page
type graphiqlPage struct {
GraphiqlVersion string
QueryString string
ResultString string
VariablesString string
OperationName string
GraphiqlVersion string
SubscriptionTransportVersion string
QueryString string
ResultString string
VariablesString string
OperationName string
Endpoint template.URL
SubscriptionEndpoint template.URL
UsingHTTP bool
UsingWS bool
}

// renderGraphiQL renders the GraphiQL GUI
func renderGraphiQL(w http.ResponseWriter, params graphql.Params) {
func renderGraphiQL(w http.ResponseWriter, params graphql.Params, handler Handler) {
t := template.New("GraphiQL")
t, err := t.Parse(graphiqlTemplate)
if err != nil {
Expand Down Expand Up @@ -50,12 +56,29 @@ func renderGraphiQL(w http.ResponseWriter, params graphql.Params) {
resString = string(result)
}

isEndpointUsingWS := strings.HasPrefix(handler.Endpoint, "ws://")
UsingHTTP := !isEndpointUsingWS
UsingWS := isEndpointUsingWS || handler.SubscriptionsEndpoint != ""
SubscriptionEndpoint := ""
if UsingWS {
if isEndpointUsingWS {
SubscriptionEndpoint = handler.Endpoint
} else {
SubscriptionEndpoint = handler.SubscriptionsEndpoint
}
}

p := graphiqlPage{
GraphiqlVersion: graphiqlVersion,
QueryString: params.RequestString,
ResultString: resString,
VariablesString: varsString,
OperationName: params.OperationName,
GraphiqlVersion: graphiqlVersion,
SubscriptionTransportVersion: subscriptionTransportVersion,
QueryString: params.RequestString,
ResultString: resString,
VariablesString: varsString,
OperationName: params.OperationName,
Endpoint: template.URL(handler.Endpoint),
SubscriptionEndpoint: template.URL(SubscriptionEndpoint),
UsingHTTP: UsingHTTP,
UsingWS: UsingWS,
}

err = t.ExecuteTemplate(w, "index", p)
Expand All @@ -68,6 +91,9 @@ func renderGraphiQL(w http.ResponseWriter, params graphql.Params) {
// graphiqlVersion is the current version of GraphiQL
const graphiqlVersion = "0.11.10"

// subscriptionTransportVersion is the current version of the subscription transport of GraphiQL
const subscriptionTransportVersion = "0.8.2"

// tmpl is the page template to render GraphiQL
const graphiqlTemplate = `
{{ define "index" }}
Expand All @@ -94,10 +120,20 @@ add "&raw" to the end of the URL within a browser.
}
</style>
<link href="//cdn.jsdelivr.net/npm/graphiql@{{ .GraphiqlVersion }}/graphiql.css" rel="stylesheet" />
<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>

{{ if .UsingHTTP }}
<script src="//cdn.jsdelivr.net/fetch/2.0.1/fetch.min.js"></script>
{{ end }}
{{ if .UsingWS }}
<script src="//unpkg.com/subscriptions-transport-ws@{{ .SubscriptionTransportVersion }}/browser/client.js"></script>
{{ end }}
{{ if and .UsingWS .UsingHTTP }}
<script src="//unpkg.com/[email protected]/browser/client.js"></script>
{{ end }}

</head>
<body>
<script>
Expand All @@ -112,10 +148,8 @@ add "&raw" to the end of the URL within a browser.
});

// 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) {
function locationQuery(params, location) {
return (location ? location: '') + '?' + Object.keys(params).map(function (key) {
return encodeURIComponent(key) + '=' +
encodeURIComponent(params[key]);
}).join('&');
Expand All @@ -134,28 +168,49 @@ add "&raw" to the end of the URL within a browser.
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'
},
body: JSON.stringify(graphQLParams),
credentials: 'include',
}).then(function (response) {
return response.text();
}).then(function (responseBody) {
try {
return JSON.parse(responseBody);
} catch (error) {
return responseBody;
}

{{ if .UsingWS }}
var subscriptionsClient = new window.SubscriptionsTransportWs.SubscriptionClient({{ .SubscriptionEndpoint }}, {
reconnect: true
});
}
var graphQLWSFetcher = subscriptionsClient.request.bind(subscriptionsClient);
{{ end }}

{{ if .UsingHTTP }}
var fetchURL = locationQuery(otherParams, {{ .Endpoint }});

// Defines a GraphQL fetcher using the fetch API.
function graphQLHttpFetcher(graphQLParams) {
return fetch(fetchURL, {
method: 'post',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify(graphQLParams),
credentials: 'include',
}).then(function (response) {
return response.text();
}).then(function (responseBody) {
try {
return JSON.parse(responseBody);
} catch (error) {
return responseBody;
}
});
}
{{ end }}

{{ if and .UsingWS .UsingHTTP }}
var fetcher = window.GraphiQLSubscriptionsFetcher.graphQLFetcher(subscriptionsClient, graphQLHttpFetcher);
{{ else }}
{{ if .UsingWS }}
var fetcher = graphQLWSFetcher;
{{ end }}
{{ if .UsingHTTP }}
var fetcher = graphQLHttpFetcher;
{{ end }}
{{ end }}

// When the query and variables string is edited, update the URL bar so
// that it can be easily shared.
Expand All @@ -181,7 +236,7 @@ add "&raw" to the end of the URL within a browser.
// Render <GraphiQL /> into the body.
ReactDOM.render(
React.createElement(GraphiQL, {
fetcher: graphQLFetcher,
fetcher: fetcher,
onEditQuery: onEditQuery,
onEditVariables: onEditVariables,
onEditOperationName: onEditOperationName,
Expand Down
34 changes: 21 additions & 13 deletions handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@ const (
)

type Handler struct {
Schema *graphql.Schema
pretty bool
graphiql bool
Schema *graphql.Schema
pretty bool
graphiql bool
Endpoint string
SubscriptionsEndpoint string
}
type RequestOptions struct {
Query string `json:"query" url:"query" schema:"query"`
Expand Down Expand Up @@ -133,7 +135,7 @@ func (h *Handler) ContextHandler(ctx context.Context, w http.ResponseWriter, r *
acceptHeader := r.Header.Get("Accept")
_, raw := r.URL.Query()["raw"]
if !raw && !strings.Contains(acceptHeader, "application/json") && strings.Contains(acceptHeader, "text/html") {
renderGraphiQL(w, params)
renderGraphiQL(w, params, *h)
return
}
}
Expand All @@ -160,16 +162,20 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}

type Config struct {
Schema *graphql.Schema
Pretty bool
GraphiQL bool
Schema *graphql.Schema
Pretty bool
GraphiQL bool
Endpoint string
SubscriptionsEndpoint string
}

func NewConfig() *Config {
return &Config{
Schema: nil,
Pretty: true,
GraphiQL: true,
Schema: nil,
Pretty: true,
GraphiQL: true,
Endpoint: "",
SubscriptionsEndpoint: "",
}
}

Expand All @@ -182,8 +188,10 @@ func New(p *Config) *Handler {
}

return &Handler{
Schema: p.Schema,
pretty: p.Pretty,
graphiql: p.GraphiQL,
Schema: p.Schema,
pretty: p.Pretty,
graphiql: p.GraphiQL,
Endpoint: p.Endpoint,
SubscriptionsEndpoint: p.SubscriptionsEndpoint,
}
}