Skip to content

Commit

Permalink
Merge branch 'main' into dave/bump-demos
Browse files Browse the repository at this point in the history
  • Loading branch information
df-wg authored Jan 22, 2025
2 parents 7b80aba + 1d7047b commit e24c8c5
Show file tree
Hide file tree
Showing 12 changed files with 124 additions and 24 deletions.
1 change: 1 addition & 0 deletions router-tests/testenv/testenv.go
Original file line number Diff line number Diff line change
Expand Up @@ -828,6 +828,7 @@ func configureRouter(listenerAddr string, testConfig *Config, routerConfig *node
core.WithGraphApiToken(graphApiToken),
core.WithDevelopmentMode(true),
core.WithPlayground(true),
core.WithPlaygroundConfig(config.PlaygroundConfig{Enabled: true}),
core.WithEngineExecutionConfig(engineExecutionConfig),
core.WithSecurityConfig(cfg.SecurityConfiguration),
core.WithCacheControlPolicy(cfg.CacheControl),
Expand Down
6 changes: 6 additions & 0 deletions router/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ Binaries are attached to the github release otherwise all images can be found [h
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.

## [0.164.1](https://github.com/wundergraph/cosmo/compare/[email protected]@0.164.1) (2025-01-20)

### Bug Fixes

* **router:** write proper line endings and header for multipart ([#1517](https://github.com/wundergraph/cosmo/issues/1517)) ([c09ecf4](https://github.com/wundergraph/cosmo/commit/c09ecf4ba01977fc1e8bef9383d68600a5d886f9)) (@df-wg)

# [0.164.0](https://github.com/wundergraph/cosmo/compare/[email protected]@0.164.0) (2025-01-19)

### Features
Expand Down
1 change: 1 addition & 0 deletions router/cmd/instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ func NewRouter(ctx context.Context, params Params, additionalOptions ...core.Opt
core.WithGraphQLPath(cfg.GraphQLPath),
core.WithModulesConfig(cfg.Modules),
core.WithGracePeriod(cfg.GracePeriod),
core.WithPlaygroundConfig(cfg.PlaygroundConfig),
core.WithPlaygroundPath(cfg.PlaygroundPath),
core.WithHealthCheckPath(cfg.HealthCheckPath),
core.WithLivenessCheckPath(cfg.LivenessCheckPath),
Expand Down
6 changes: 3 additions & 3 deletions router/core/graph_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -235,8 +235,8 @@ func newGraphServer(ctx context.Context, r *Router, routerConfig *nodev1.RouterC

// We mount the playground once here when we don't have a conflict with the websocket handler
// If we have a conflict, we mount the playground during building the individual muxes
if s.playgroundHandler != nil && s.graphqlPath != s.playgroundPath {
httpRouter.Get(r.playgroundPath, s.playgroundHandler(nil).ServeHTTP)
if s.playgroundHandler != nil && s.graphqlPath != s.playgroundConfig.Path {
httpRouter.Get(r.playgroundConfig.Path, s.playgroundHandler(nil).ServeHTTP)
}

httpRouter.Get(s.healthCheckPath, r.healthcheck.Liveness())
Expand Down Expand Up @@ -1049,7 +1049,7 @@ func (s *graphServer) buildGraphMux(ctx context.Context,

// When the playground path is equal to the graphql path, we need to handle
// ws upgrades and html requests on the same route.
if s.playground && s.graphqlPath == s.playgroundPath {
if s.playgroundConfig.Enabled && s.graphqlPath == s.playgroundConfig.Path {
httpRouter.Use(s.playgroundHandler, wsMiddleware)
} else {
httpRouter.Use(wsMiddleware)
Expand Down
35 changes: 27 additions & 8 deletions router/core/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ type (
healthCheckPath string
readinessCheckPath string
livenessCheckPath string
playgroundConfig config.PlaygroundConfig
cacheControlPolicy config.CacheControlPolicy
routerConfigPollerConfig *RouterConfigPollerConfig
cdnConfig config.CDNConfiguration
Expand Down Expand Up @@ -258,8 +259,18 @@ func NewRouter(opts ...Option) (*Router, error) {
r.graphqlWebURL = r.graphqlPath
}

if r.playgroundPath == "" {
r.playgroundPath = "/"
// this is set via the deprecated method
if !r.playground {
r.playgroundConfig.Enabled = r.playground
r.logger.Warn("The playground_enabled option is deprecated. Use the playground.enabled option in the config instead.")
}
if r.playgroundPath != "" && r.playgroundPath != "/" {
r.playgroundConfig.Path = r.playgroundPath
r.logger.Warn("The playground_path option is deprecated. Use the playground.path option in the config instead.")
}

if r.playgroundConfig.Path == "" {
r.playgroundConfig.Path = "/"
}

if r.instanceID == "" {
Expand Down Expand Up @@ -598,7 +609,7 @@ func (r *Router) newServer(ctx context.Context, cfg *nodev1.RouterConfig) error
func (r *Router) listenAndServe(cfg *nodev1.RouterConfig) error {
r.logger.Info("Server listening and serving",
zap.String("listen_addr", r.listenAddr),
zap.Bool("playground", r.playground),
zap.Bool("playground", r.playgroundConfig.Enabled),
zap.Bool("introspection", r.introspection),
zap.String("config_version", cfg.GetVersion()),
)
Expand Down Expand Up @@ -854,15 +865,16 @@ func (r *Router) bootstrap(ctx context.Context) error {
debug.ReportMemoryUsage(ctx, r.logger)
}

if r.playground {
playgroundUrl, err := url.JoinPath(r.baseURL, r.playgroundPath)
if r.playgroundConfig.Enabled {
playgroundUrl, err := url.JoinPath(r.baseURL, r.playgroundConfig.Path)
if err != nil {
return fmt.Errorf("failed to join playground url: %w", err)
}
r.logger.Info("Serving GraphQL playground", zap.String("url", playgroundUrl))
r.playgroundHandler = graphiql.NewPlayground(&graphiql.PlaygroundOptions{
Html: graphiql.PlaygroundHTML(),
GraphqlURL: r.graphqlWebURL,
Html: graphiql.PlaygroundHTML(),
GraphqlURL: r.graphqlWebURL,
ConcurrencyLimit: int64(r.playgroundConfig.ConcurrencyLimit),
})
}

Expand Down Expand Up @@ -1128,7 +1140,7 @@ func (r *Router) Start(ctx context.Context) error {
return err
}

if r.playground {
if r.playgroundConfig.Enabled {
graphqlEndpointURL, err := url.JoinPath(r.baseURL, r.graphqlPath)
if err != nil {
return fmt.Errorf("failed to join graphql endpoint url: %w", err)
Expand Down Expand Up @@ -1381,6 +1393,13 @@ func WithPlaygroundPath(p string) Option {
}
}

// WithPlaygroundPath sets the path where the GraphQL Playground is served.
func WithPlaygroundConfig(c config.PlaygroundConfig) Option {
return func(r *Router) {
r.playgroundConfig = c
}
}

// WithConfigPoller sets the poller client to fetch the router config. If not set, WithConfigPollerConfig should be set.
func WithConfigPoller(cf configpoller.ConfigPoller) Option {
return func(r *Router) {
Expand Down
44 changes: 34 additions & 10 deletions router/internal/graphiql/playgroundhandler.go
Original file line number Diff line number Diff line change
@@ -1,30 +1,52 @@
package graphiql

import (
"bytes"
"golang.org/x/sync/semaphore"
"net/http"
"strconv"
"strings"
)

type PlaygroundOptions struct {
Html string
GraphqlURL string
Html string
GraphqlURL string
ConcurrencyLimit int64
}

type Playground struct {
next http.Handler
opts *PlaygroundOptions
next http.Handler
opts *PlaygroundOptions
templateBytes []byte
sem *semaphore.Weighted
}

var (
defaultLimitUsage = int64(10)
)

func NewPlayground(opts *PlaygroundOptions) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return &Playground{
limit := opts.ConcurrencyLimit
if limit == 0 {
limit = defaultLimitUsage
}
p := &Playground{
next: next,
opts: opts,
sem: semaphore.NewWeighted(limit),
}
p.initPlayground()
return p
}
}

func (p *Playground) initPlayground() {
tpl := strings.Replace(p.opts.Html, "{{graphqlURL}}", p.opts.GraphqlURL, -1)
play := []byte(tpl)
p.templateBytes = play
}

func (p *Playground) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// Only serve the playground if the request is for text/html
// if not, just pass through to the next handler
Expand All @@ -35,12 +57,14 @@ func (p *Playground) ServeHTTP(w http.ResponseWriter, r *http.Request) {
return
}

tpl := strings.Replace(p.opts.Html, "{{graphqlURL}}", p.opts.GraphqlURL, -1)
resp := []byte(tpl)

if err := p.sem.Acquire(r.Context(), 1); err != nil {
http.Error(w, "Too many requests", http.StatusTooManyRequests)
return
}
defer p.sem.Release(1) // Ensure the semaphore slot is released
w.Header().Set("Content-Type", "text/html; charset=utf-8")
w.Header().Set("Content-Length", strconv.Itoa(len(resp)))
w.Header().Set("Content-Length", strconv.Itoa(len(p.templateBytes)))

w.WriteHeader(http.StatusOK)
_, _ = w.Write(resp)
_, _ = bytes.NewBuffer(p.templateBytes).WriteTo(w)
}
2 changes: 1 addition & 1 deletion router/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "router",
"version": "0.164.0",
"version": "0.164.1",
"private": true,
"description": "Placeholder package to simplify versioning and releasing with lerna.",
"keywords": [
Expand Down
7 changes: 7 additions & 0 deletions router/pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -805,6 +805,7 @@ type Config struct {

ListenAddr string `yaml:"listen_addr" envDefault:"localhost:3002" env:"LISTEN_ADDR"`
ControlplaneURL string `yaml:"controlplane_url" envDefault:"https://cosmo-cp.wundergraph.com" env:"CONTROLPLANE_URL"`
PlaygroundConfig PlaygroundConfig `yaml:"playground,omitempty"`
PlaygroundEnabled bool `yaml:"playground_enabled" envDefault:"true" env:"PLAYGROUND_ENABLED"`
IntrospectionEnabled bool `yaml:"introspection_enabled" envDefault:"true" env:"INTROSPECTION_ENABLED"`
QueryPlansEnabled bool `yaml:"query_plans_enabled" envDefault:"true" env:"QUERY_PLANS_ENABLED"`
Expand Down Expand Up @@ -851,6 +852,12 @@ type Config struct {
ClientHeader ClientHeader `yaml:"client_header"`
}

type PlaygroundConfig struct {
Enabled bool `yaml:"enabled" envDefault:"true" env:"PLAYGROUND_ENABLED"`
Path string `yaml:"path" envDefault:"/" env:"PLAYGROUND_PATH"`
ConcurrencyLimit int `yaml:"concurrency_limit,omitempty" envDefault:"10" env:"PLAYGROUND_CONCURRENCY_LIMIT"`
}

type LoadResult struct {
Config Config
DefaultLoaded bool
Expand Down
32 changes: 30 additions & 2 deletions router/pkg/config/config.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -1075,10 +1075,36 @@
"default": "https://cosmo-cp.wundergraph.com",
"format": "http-url"
},
"playground": {
"type": "object",
"description": "The configuration for the playground. The playground is a web-based GraphQL IDE that allows you to interact with the GraphQL API.",
"additionalProperties": false,
"properties": {
"enabled": {
"type": "boolean",
"description": "Enable the GraphQL Playground. The GraphQL Playground is a web-based GraphQL IDE that allows you to interact with the GraphQL API. The default value is true. If the value is false, the GraphQL Playground is disabled.",
"default": true
},
"path": {
"type": "string",
"format": "x-uri",
"default": "/",
"description": "The path of the GraphQL Playground. The GraphQL Playground is a web-based GraphQL IDE that allows you to interact with the GraphQL API. The default value is '/'."
},
"concurrency_limit": {
"type": "integer",
"description": "The concurrency limit for loading the playground. This shouldn't impact normal usage.",
"default": 10,
"minimum": 1
}
}
},
"playground_enabled": {
"type": "boolean",
"description": "Enable the GraphQL Playground. The GraphQL Playground is a web-based GraphQL IDE that allows you to interact with the GraphQL API. The default value is true. If the value is false, the GraphQL Playground is disabled.",
"default": true
"default": true,
"deprecated": true,
"deprecationMessage": "playground_enabled is deprecated. Please use the playground.enabled configuration instead."
},
"introspection_enabled": {
"type": "boolean",
Expand Down Expand Up @@ -1159,7 +1185,9 @@
"type": "string",
"format": "x-uri",
"default": "/",
"description": "The path of the GraphQL Playground. The GraphQL Playground is a web-based GraphQL IDE that allows you to interact with the GraphQL API. The default value is '/'."
"description": "The path of the GraphQL Playground. The GraphQL Playground is a web-based GraphQL IDE that allows you to interact with the GraphQL API. The default value is '/'.",
"deprecated": true,
"deprecationMessage": "playground_path is deprecated. Please use the playground.path configuration instead."
},
"file_upload": {
"type": "object",
Expand Down
4 changes: 4 additions & 0 deletions router/pkg/config/fixtures/full.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ listen_addr: "localhost:3002"
controlplane_url: "https://cosmo-cp.wundergraph.com"
playground_enabled: true
playground_path: "/"
playground:
enabled: false
path: "/my-playground"
concurrency_limit: 1500
introspection_enabled: true
json_log: true
shutdown_delay: 15s
Expand Down
5 changes: 5 additions & 0 deletions router/pkg/config/testdata/config_defaults.json
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,11 @@
},
"ListenAddr": "localhost:3002",
"ControlplaneURL": "https://cosmo-cp.wundergraph.com",
"PlaygroundConfig": {
"Enabled": true,
"Path": "/",
"ConcurrencyLimit": 10
},
"PlaygroundEnabled": true,
"IntrospectionEnabled": true,
"QueryPlansEnabled": true,
Expand Down
5 changes: 5 additions & 0 deletions router/pkg/config/testdata/config_full.json
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,11 @@
},
"ListenAddr": "localhost:3002",
"ControlplaneURL": "https://cosmo-cp.wundergraph.com",
"PlaygroundConfig": {
"Enabled": false,
"Path": "/my-playground",
"ConcurrencyLimit": 1500
},
"PlaygroundEnabled": true,
"IntrospectionEnabled": true,
"QueryPlansEnabled": true,
Expand Down

0 comments on commit e24c8c5

Please sign in to comment.