Skip to content
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

feat: allow custom playground path #452

Merged
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
1 change: 1 addition & 0 deletions router/cmd/instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ func NewRouter(params Params, additionalOptions ...core.Option) (*core.Router, e
core.WithGraphQLPath(cfg.GraphQLPath),
core.WithModulesConfig(cfg.Modules),
core.WithGracePeriod(cfg.GracePeriod),
core.WithPlaygroundPath(cfg.PlaygroundPath),
core.WithHealthCheckPath(cfg.HealthCheckPath),
core.WithLivenessCheckPath(cfg.LivenessCheckPath),
core.WithGraphQLMetrics(&core.GraphQLMetricsConfig{
Expand Down
35 changes: 29 additions & 6 deletions router/core/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ type (
listenAddr string
baseURL string
graphqlWebURL string
playgroundPath string
graphqlPath string
playground bool
introspection bool
Expand Down Expand Up @@ -173,6 +174,10 @@ func NewRouter(opts ...Option) (*Router, error) {
r.graphqlWebURL = r.graphqlPath
}

if r.playgroundPath == "" {
r.playgroundPath = "/"
}

// Default values for trace and metric config

if r.traceConfig == nil {
Expand Down Expand Up @@ -870,7 +875,11 @@ func (r *Router) newServer(ctx context.Context, routerConfig *nodev1.RouterConfi
var graphqlPlaygroundHandler func(http.Handler) http.Handler

if r.playground {
r.logger.Info("Serving GraphQL playground", zap.String("url", r.baseURL))
playgroundUrl, err := url.JoinPath(r.baseURL, r.playgroundPath)
if err != nil {
return nil, fmt.Errorf("failed to join playground url: %w", err)
}
r.logger.Info("Serving GraphQL playground", zap.String("url", playgroundUrl))
graphqlPlaygroundHandler = graphiql.NewPlayground(&graphiql.PlaygroundOptions{
Log: r.logger,
Html: graphiql.PlaygroundHTML(),
Expand Down Expand Up @@ -925,27 +934,34 @@ func (r *Router) newServer(ctx context.Context, routerConfig *nodev1.RouterConfi
graphqlChiRouter := chi.NewRouter()

// When the playground path is equal to the graphql path, we need to handle
// ws upgrades and html requests on the same route
if r.playground && r.graphqlPath == "/" {
// ws upgrades and html requests on the same route.
if r.playground && r.graphqlPath == r.playgroundPath {
graphqlChiRouter.Use(graphqlPlaygroundHandler, wsMiddleware)
} else {
if r.playground {
httpRouter.Get("/", graphqlPlaygroundHandler(nil).ServeHTTP)
httpRouter.Get(r.playgroundPath, graphqlPlaygroundHandler(nil).ServeHTTP)
}
graphqlChiRouter.Use(wsMiddleware)
}

graphqlChiRouter.Use(graphqlPreHandler.Handler)

// Built in and custom modules
graphqlChiRouter.Use(r.routerMiddlewares...)

graphqlChiRouter.Post("/", graphqlHandler.ServeHTTP)

// Serve GraphQL. MetricStore are collected after the request is handled and classified as r GraphQL request.
httpRouter.Mount(r.graphqlPath, graphqlChiRouter)

graphqlEndpointURL, err := url.JoinPath(r.baseURL, r.graphqlPath)
if err != nil {
return nil, fmt.Errorf("failed to join graphql endpoint url: %w", err)
}

r.logger.Info("GraphQL endpoint",
zap.String("method", http.MethodPost),
zap.String("url", r.baseURL+r.graphqlPath),
zap.String("url", graphqlEndpointURL),
)

ro.server = &http.Server{
Expand Down Expand Up @@ -1117,7 +1133,7 @@ func WithCors(corsOpts *cors.Config) Option {
}
}

// WithGraphQLPath sets the path to the GraphQL endpoint.
// WithGraphQLPath sets the path where the GraphQL endpoint is served.
func WithGraphQLPath(p string) Option {
return func(r *Router) {
r.graphqlPath = p
Expand All @@ -1133,6 +1149,13 @@ func WithGraphQLWebURL(p string) Option {
}
}

// WithPlaygroundPath sets the path where the GraphQL Playground is served.
func WithPlaygroundPath(p string) Option {
return func(r *Router) {
r.playgroundPath = p
}
}

func WithConfigPoller(cf configpoller.ConfigPoller) Option {
return func(r *Router) {
r.configPoller = cf
Expand Down
9 changes: 2 additions & 7 deletions router/internal/graphiql/playgroundhandler.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,8 @@ func NewPlayground(opts *PlaygroundOptions) func(http.Handler) http.Handler {

func (p *Playground) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// Only serve the playground if the request is for text/html
// This is especially important for Upgrade websocket requests
// when the graphql endpoint is on the same path as the playground
if isWsUpgradeRequest(r) || !strings.Contains(r.Header.Get("Accept"), "text/html") {
// if not, just pass through to the next handler
if !strings.Contains(strings.ToLower(r.Header.Get("Accept")), "text/html") {
if p.next != nil {
p.next.ServeHTTP(w, r)
}
Expand All @@ -47,7 +46,3 @@ func (p *Playground) ServeHTTP(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
_, _ = w.Write(resp)
}

func isWsUpgradeRequest(r *http.Request) bool {
return r.Header.Get("Upgrade") == "websocket"
}
3 changes: 2 additions & 1 deletion router/pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,8 @@ type Config struct {
HealthCheckPath string `yaml:"health_check_path" default:"/health" envconfig:"HEALTH_CHECK_PATH" validate:"uri"`
ReadinessCheckPath string `yaml:"readiness_check_path" default:"/health/ready" envconfig:"READINESS_CHECK_PATH" validate:"uri"`
LivenessCheckPath string `yaml:"liveness_check_path" default:"/health/live" envconfig:"LIVENESS_CHECK_PATH" validate:"uri"`
GraphQLPath string `yaml:"graphql_path" default:"/graphql" envconfig:"GRAPHQL_PATH"`
GraphQLPath string `yaml:"graphql_path" default:"/graphql" validate:"uri" envconfig:"GRAPHQL_PATH"`
PlaygroundPath string `yaml:"playground_path" default:"/" validate:"uri" envconfig:"PLAYGROUND_PATH"`
Authentication AuthenticationConfiguration `yaml:"authentication"`
Authorization AuthorizationConfiguration `yaml:"authorization"`
LocalhostFallbackInsideDocker bool `yaml:"localhost_fallback_inside_docker" default:"true" envconfig:"LOCALHOST_FALLBACK_INSIDE_DOCKER"`
Expand Down
Loading