-
Notifications
You must be signed in to change notification settings - Fork 3.7k
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(server/v2): Add Swagger UI support for server/v2 #23092
base: main
Are you sure you want to change the base?
Changes from 5 commits
4ed5650
2ac2555
8930230
c812722
d0c20bc
ac5982a
f96dafe
9859c38
e96e5bb
e733362
34eab36
eec89d7
654cc99
c9d6544
16556c1
2021911
93dc4b6
446c0f8
c038d6c
0938d61
2466ab5
8a042b2
8ccb884
9659ba7
8f3017c
0fa0b6b
35f2fad
44da58f
98f2d85
a3dbbe8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package swagger | ||
|
||
import ( | ||
"cosmossdk.io/core/server" | ||
) | ||
|
||
const ServerName = "swagger" | ||
|
||
type Config struct { | ||
Enable bool `toml:"enable" mapstructure:"enable"` | ||
Address string `toml:"address" mapstructure:"address"` | ||
Path string `toml:"path" mapstructure:"path"` | ||
} | ||
|
||
func DefaultConfig() *Config { | ||
return &Config{ | ||
Enable: true, | ||
Address: "localhost:8080", | ||
Path: "/swagger/", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do we need a path? I think keeping /swagger by default is great. |
||
} | ||
} | ||
|
||
type CfgOption func(*Config) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
package swagger | ||
|
||
import ( | ||
"net/http" | ||
"path" | ||
"path/filepath" | ||
"strings" | ||
|
||
"github.com/rakyll/statik/fs" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add Required Dependency for Statik The pipeline indicates that the module Would you like assistance creating a diff to add 🧰 Tools🪛 GitHub Actions: Dependency Review[error] 10-10: Missing required module: github.com/rakyll/statik/fs. Module needs to be added to the project dependencies. |
||
) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing time import import (
"net/http"
"path"
"path/filepath"
"strings"
"github.com/rakyll/statik/fs"
+ "time"
)
|
||
|
||
// Handler returns an HTTP handler that serves the Swagger UI files | ||
func Handler(statikFS http.FileSystem) http.HandlerFunc { | ||
return func(w http.ResponseWriter, r *http.Request) { | ||
// If the path is empty or "/", show index.html | ||
if r.URL.Path == "/" || r.URL.Path == "" { | ||
r.URL.Path = "/index.html" | ||
} | ||
|
||
// Clearing the path | ||
urlPath := path.Clean(r.URL.Path) | ||
|
||
// Opening the file from statikFS | ||
f, err := statikFS.Open(urlPath) | ||
if err != nil { | ||
w.WriteHeader(http.StatusNotFound) | ||
return | ||
} | ||
defer f.Close() | ||
|
||
// Determining the content-type | ||
ext := strings.ToLower(filepath.Ext(urlPath)) | ||
switch ext { | ||
case ".html": | ||
w.Header().Set("Content-Type", "text/html") | ||
case ".css": | ||
w.Header().Set("Content-Type", "text/css") | ||
case ".js": | ||
w.Header().Set("Content-Type", "application/javascript") | ||
case ".json": | ||
w.Header().Set("Content-Type", "application/json") | ||
} | ||
|
||
http.ServeContent(w, r, urlPath, time.Time{}, f) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
package swagger | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"net/http" | ||
|
||
"cosmossdk.io/core/server" | ||
"cosmossdk.io/core/transaction" | ||
"cosmossdk.io/log" | ||
serverv2 "cosmossdk.io/server/v2" | ||
) | ||
|
||
type Server[T transaction.Tx] struct { | ||
logger log.Logger | ||
config *Config | ||
cfgOptions []CfgOption | ||
server *http.Server | ||
} | ||
|
||
func New[T transaction.Tx]( | ||
logger log.Logger, | ||
cfg server.ConfigMap, | ||
cfgOptions ...CfgOption, | ||
) (*Server[T], error) { | ||
srv := &Server[T]{ | ||
logger: logger.With(log.ModuleKey, ServerName), | ||
cfgOptions: cfgOptions, | ||
} | ||
|
||
serverCfg := srv.Config().(*Config) | ||
julienrbrt marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if len(cfg) > 0 { | ||
if err := serverv2.UnmarshalSubConfig(cfg, srv.Name(), &serverCfg); err != nil { | ||
return nil, fmt.Errorf("failed to unmarshal config: %w", err) | ||
} | ||
} | ||
srv.config = serverCfg | ||
|
||
mux := http.NewServeMux() | ||
mux.Handle(srv.config.Path, NewSwaggerHandler()) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Handle the undefined The function Would you like assistance creating a 🧰 Tools🪛 golangci-lint (1.62.2)40-40: undefined: NewSwaggerHandler (typecheck) |
||
|
||
srv.server = &http.Server{ | ||
Addr: srv.config.Address, | ||
Handler: mux, | ||
} | ||
|
||
return srv, nil | ||
} | ||
|
||
func (s *Server[T]) Name() string { | ||
return ServerName | ||
} | ||
|
||
func (s *Server[T]) Start(ctx context.Context) error { | ||
if !s.config.Enable { | ||
s.logger.Info("swagger server is disabled via config") | ||
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 | ||
} | ||
|
||
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) | ||
} |
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hi, thanks for tackling this. The wiring shouldn't be done in the main server, but api/swagger should be its own server component. Check out the other servers. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
oh you're absolutely right, gonna fix it soon! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @julienrbrt sorry for mess in commits, I had to redo some things but now everything should be fine |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,12 +8,14 @@ import ( | |
"path/filepath" | ||
"strings" | ||
|
||
"github.com/cosmos/cosmos-sdk/server/v2/api/swagger" | ||
"github.com/pelletier/go-toml/v2" | ||
"github.com/spf13/cobra" | ||
"github.com/spf13/pflag" | ||
|
||
"cosmossdk.io/core/transaction" | ||
"cosmossdk.io/log" | ||
"github.com/rakyll/statik/fs" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Check for missing imports import (
"context"
"errors"
"fmt"
"os"
"path/filepath"
"strings"
+ "net/http"
"github.com/cosmos/cosmos-sdk/server/v2/api/swagger"
"github.com/pelletier/go-toml/v2"
"github.com/spf13/cobra"
|
||
) | ||
|
||
// ServerComponent is a server component that can be started and stopped. | ||
|
@@ -73,6 +75,7 @@ var _ ServerComponent[transaction.Tx] = (*Server[transaction.Tx])(nil) | |
type Server[T transaction.Tx] struct { | ||
components []ServerComponent[T] | ||
config ServerConfig | ||
router *http.ServeMux | ||
} | ||
|
||
func NewServer[T transaction.Tx]( | ||
|
@@ -82,6 +85,7 @@ func NewServer[T transaction.Tx]( | |
return &Server[T]{ | ||
config: config, | ||
components: components, | ||
router: http.NewServeMux(), | ||
} | ||
} | ||
|
||
|
@@ -242,3 +246,18 @@ func (s *Server[T]) StartFlags() []*pflag.FlagSet { | |
|
||
return flags | ||
} | ||
|
||
func (s *Server[T]) setupSwagger() error { | ||
cfg := s.config.API.Swagger | ||
if !cfg.Enable { | ||
return nil | ||
} | ||
|
||
statikFS, err := fs.New() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
s.router.PathPrefix(cfg.Path).Handler(swagger.Handler(statikFS)) | ||
return nil | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's use something else than 8080 by default