feat(server/v2): add swagger server component (#23486)

This commit is contained in:
Julien Robert
2025-01-24 09:24:26 +00:00
committed by GitHub
parent 5e58330196
commit 0cc73ba1b2
14 changed files with 1310 additions and 1349 deletions
+34
View File
@@ -0,0 +1,34 @@
package swagger
// Config defines the configuration for the Swagger UI server
type Config struct {
// Enable enables/disables the Swagger UI server
Enable bool `mapstructure:"enable" toml:"enable" comment:"Enable enables/disables the Swagger UI server"`
// Address defines the server address to bind to
Address string `mapstructure:"address" toml:"address" comment:"Address defines the server address to bind to"`
}
// DefaultConfig returns the default configuration
func DefaultConfig() *Config {
return &Config{
Enable: true,
Address: "localhost:8090",
}
}
// CfgOption defines a function for configuring the settings
type CfgOption func(*Config)
// OverwriteDefaultConfig overwrites the default config with the new config.
func OverwriteDefaultConfig(newCfg *Config) CfgOption {
return func(cfg *Config) {
*cfg = *newCfg
}
}
// Disable the grpc server by default (default enabled).
func Disable() CfgOption {
return func(cfg *Config) {
cfg.Enable = false
}
}
+14
View File
@@ -0,0 +1,14 @@
/*
Package swagger provides Swagger UI server/v2 component.
Example usage in commands.go:
swaggerServer, err := swaggerv2.New[T](
logger.With(log.ModuleKey, "swagger"),
deps.GlobalConfig,
swaggerv2.CfgOption(func(cfg *swaggerv2.Config) {
cfg.SwaggerUI = docs.SwaggerUI
}),
)
*/
package swagger
+22
View File
@@ -0,0 +1,22 @@
package swagger
import (
"io/fs"
"net/http"
)
type swaggerHandler struct {
swaggerFS fs.FS
}
func (h *swaggerHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// CORS headers
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, OPTIONS")
if r.Method == http.MethodOptions {
return
}
http.StripPrefix("/swagger/", http.FileServer(http.FS(h.swaggerFS))).ServeHTTP(w, r)
}
+110
View File
@@ -0,0 +1,110 @@
package swagger
import (
"context"
"fmt"
"io/fs"
"net/http"
"cosmossdk.io/core/server"
"cosmossdk.io/core/transaction"
"cosmossdk.io/log"
serverv2 "cosmossdk.io/server/v2"
)
var (
_ serverv2.ServerComponent[transaction.Tx] = (*Server[transaction.Tx])(nil)
_ serverv2.HasConfig = (*Server[transaction.Tx])(nil)
)
const ServerName = "swagger"
// Server represents a Swagger UI server
type Server[T transaction.Tx] struct {
logger log.Logger
config *Config
cfgOptions []CfgOption
server *http.Server
}
// New creates a new Swagger UI server
func New[T transaction.Tx](
logger log.Logger,
swaggerUI fs.FS,
config server.ConfigMap,
cfgOptions ...CfgOption,
) (*Server[T], error) {
s := &Server[T]{
logger: logger.With(log.ModuleKey, ServerName),
cfgOptions: cfgOptions,
}
serverCfg := s.Config().(*Config)
if len(config) > 0 {
if err := serverv2.UnmarshalSubConfig(config, s.Name(), &serverCfg); err != nil {
return s, fmt.Errorf("failed to unmarshal config: %w", err)
}
}
s.config = serverCfg
mux := http.NewServeMux()
mux.Handle("/swagger/", &swaggerHandler{
swaggerFS: swaggerUI,
})
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "/swagger/", http.StatusMovedPermanently)
})
s.server = &http.Server{
Addr: s.config.Address,
Handler: mux,
}
return s, nil
}
// Name returns the server's name
func (s *Server[T]) Name() string {
return ServerName
}
// Config returns the server configuration
func (s *Server[T]) Config() any {
if s.config == nil || s.config.Address == "" {
cfg := DefaultConfig()
// overwrite the default config with the provided options
for _, opt := range s.cfgOptions {
opt(cfg)
}
return cfg
}
return s.config
}
// Start starts the server
func (s *Server[T]) Start(ctx context.Context) error {
if !s.config.Enable {
s.logger.Info(fmt.Sprintf("%s server is disabled via config", s.Name()))
return nil
}
s.logger.Info("starting swagger server...", "address", s.config.Address)
if err := s.server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
return fmt.Errorf("failed to start swagger server: %w", err)
}
return nil
}
// Stop stops the server
func (s *Server[T]) Stop(ctx context.Context) error {
if !s.config.Enable {
return nil
}
s.logger.Info("stopping swagger server...", "address", s.config.Address)
return s.server.Shutdown(ctx)
}