feat(server/v2): add swagger server component (#23486)
This commit is contained in:
parent
5e58330196
commit
0cc73ba1b2
@ -80,14 +80,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"url": "./tmp-swagger-gen/cosmos/params/v1beta1/query.swagger.json",
|
||||
"operationIds": {
|
||||
"rename": {
|
||||
"Params": "Params"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"url": "./tmp-swagger-gen/cosmos/slashing/v1beta1/query.swagger.json",
|
||||
"operationIds": {
|
||||
@ -174,6 +166,15 @@
|
||||
},
|
||||
{
|
||||
"url": "./tmp-swagger-gen/cosmos/app/v1alpha1/query.swagger.json"
|
||||
},
|
||||
{
|
||||
"url": "./tmp-swagger-gen/cosmos/protocolpool/v1/query.swagger.json",
|
||||
"operationIds": {
|
||||
"rename": {
|
||||
"Params": "ProtocolPoolParams",
|
||||
"CommunityPool": "ProtocolPoolCommunityPool"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,19 @@
|
||||
package docs
|
||||
|
||||
import "embed"
|
||||
import (
|
||||
"embed"
|
||||
"io/fs"
|
||||
)
|
||||
|
||||
//go:embed swagger-ui
|
||||
var SwaggerUI embed.FS
|
||||
|
||||
// GetSwaggerFS returns the embedded Swagger UI filesystem
|
||||
func GetSwaggerFS() fs.FS {
|
||||
root, err := fs.Sub(SwaggerUI, "swagger-ui")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return root
|
||||
}
|
||||
|
||||
8
client/docs/swagger-ui/index.html
vendored
8
client/docs/swagger-ui/index.html
vendored
@ -1,10 +1,12 @@
|
||||
<!doctype html> <!-- Important: must specify -->
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"> <!-- Important: rapi-doc uses utf8 characters -->
|
||||
<meta charset="utf-8" />
|
||||
<!-- Important: rapi-doc uses utf8 characters -->
|
||||
<script type="module" src="rapidoc-min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<rapi-doc spec-url = "swagger.yaml"> </rapi-doc>
|
||||
<rapi-doc spec-url="swagger.yaml" server-url="http://localhost:1317">
|
||||
</rapi-doc>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
2336
client/docs/swagger-ui/swagger.yaml
vendored
2336
client/docs/swagger-ui/swagger.yaml
vendored
File diff suppressed because it is too large
Load Diff
@ -22,6 +22,8 @@ Each entry must include the Github issue reference in the following format:
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
* [#23486](https://github.com/cosmos/cosmos-sdk/pull/23486) Add `server/v2/api/swagger` server component.
|
||||
|
||||
## [v2.0.0-beta.2](https://github.com/cosmos/cosmos-sdk/releases/tag/server/v2.0.0-beta.2)
|
||||
|
||||
* [#23411](https://github.com/cosmos/cosmos-sdk/pull/23411) Use only command name in `IsAppRequired()` instead of command usage
|
||||
|
||||
@ -23,7 +23,6 @@ import (
|
||||
"google.golang.org/protobuf/reflect/protoreflect"
|
||||
|
||||
"cosmossdk.io/core/transaction"
|
||||
"cosmossdk.io/log"
|
||||
"cosmossdk.io/server/v2/appmanager"
|
||||
)
|
||||
|
||||
@ -47,7 +46,7 @@ type queryMetadata struct {
|
||||
|
||||
// mountHTTPRoutes registers handlers for from proto HTTP annotations to the http.ServeMux, using runtime.ServeMux as a fallback/
|
||||
// last ditch effort router.
|
||||
func mountHTTPRoutes[T transaction.Tx](logger log.Logger, httpMux *http.ServeMux, fallbackRouter *runtime.ServeMux, am appmanager.AppManager[T]) error {
|
||||
func mountHTTPRoutes[T transaction.Tx](httpMux *http.ServeMux, fallbackRouter *runtime.ServeMux, am appmanager.AppManager[T]) error {
|
||||
annotationMapping, err := newHTTPAnnotationMapping()
|
||||
if err != nil {
|
||||
return err
|
||||
@ -56,12 +55,12 @@ func mountHTTPRoutes[T transaction.Tx](logger log.Logger, httpMux *http.ServeMux
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
registerMethods[T](logger, httpMux, am, fallbackRouter, annotationToMetadata)
|
||||
registerMethods[T](httpMux, am, fallbackRouter, annotationToMetadata)
|
||||
return nil
|
||||
}
|
||||
|
||||
// registerMethods registers the endpoints specified in the annotation mapping to the http.ServeMux.
|
||||
func registerMethods[T transaction.Tx](logger log.Logger, mux *http.ServeMux, am appmanager.AppManager[T], fallbackRouter *runtime.ServeMux, annotationToMetadata map[string]queryMetadata) {
|
||||
func registerMethods[T transaction.Tx](mux *http.ServeMux, am appmanager.AppManager[T], fallbackRouter *runtime.ServeMux, annotationToMetadata map[string]queryMetadata) {
|
||||
// register the fallback handler. this will run if the mux isn't able to get a match from the registrations below.
|
||||
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
fallbackRouter.ServeHTTP(w, r)
|
||||
|
||||
@ -76,7 +76,7 @@ func New[T transaction.Tx](
|
||||
s.logger = logger.With(log.ModuleKey, s.Name())
|
||||
s.config = serverCfg
|
||||
mux := http.NewServeMux()
|
||||
err := mountHTTPRoutes[T](logger, mux, s.GRPCGatewayRouter, appManager)
|
||||
err := mountHTTPRoutes(mux, s.GRPCGatewayRouter, appManager)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to register gRPC gateway annotations: %w", err)
|
||||
}
|
||||
|
||||
34
server/v2/api/swagger/config.go
Normal file
34
server/v2/api/swagger/config.go
Normal 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
server/v2/api/swagger/doc.go
Normal file
14
server/v2/api/swagger/doc.go
Normal 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
server/v2/api/swagger/handler.go
Normal file
22
server/v2/api/swagger/handler.go
Normal 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
server/v2/api/swagger/server.go
Normal file
110
server/v2/api/swagger/server.go
Normal 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)
|
||||
}
|
||||
@ -29,7 +29,7 @@ type Server[T transaction.Tx] struct {
|
||||
}
|
||||
|
||||
// New creates a new telemetry server.
|
||||
func New[T transaction.Tx](cfg server.ConfigMap, logger log.Logger, enableTelemetry func(), cfgOptions ...CfgOption) (*Server[T], error) {
|
||||
func New[T transaction.Tx](logger log.Logger, enableTelemetry func(), cfg server.ConfigMap, cfgOptions ...CfgOption) (*Server[T], error) {
|
||||
srv := &Server[T]{}
|
||||
serverCfg := srv.Config().(*Config)
|
||||
if len(cfg) > 0 {
|
||||
|
||||
@ -15,6 +15,7 @@ import (
|
||||
grpcserver "cosmossdk.io/server/v2/api/grpc"
|
||||
"cosmossdk.io/server/v2/api/grpcgateway"
|
||||
"cosmossdk.io/server/v2/api/rest"
|
||||
"cosmossdk.io/server/v2/api/swagger"
|
||||
"cosmossdk.io/server/v2/api/telemetry"
|
||||
"cosmossdk.io/server/v2/cometbft"
|
||||
serverstore "cosmossdk.io/server/v2/store"
|
||||
@ -24,6 +25,7 @@ import (
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/client/config"
|
||||
"github.com/cosmos/cosmos-sdk/client/debug"
|
||||
"github.com/cosmos/cosmos-sdk/client/docs"
|
||||
"github.com/cosmos/cosmos-sdk/client/grpc/cmtservice"
|
||||
nodeservice "github.com/cosmos/cosmos-sdk/client/grpc/node"
|
||||
"github.com/cosmos/cosmos-sdk/client/keys"
|
||||
@ -85,6 +87,7 @@ func InitRootCmd[T transaction.Tx](
|
||||
&telemetry.Server[T]{},
|
||||
&rest.Server[T]{},
|
||||
&grpcgateway.Server[T]{},
|
||||
&swagger.Server[T]{},
|
||||
)
|
||||
}
|
||||
|
||||
@ -122,7 +125,12 @@ func InitRootCmd[T transaction.Tx](
|
||||
return nil, err
|
||||
}
|
||||
|
||||
telemetryServer, err := telemetry.New[T](deps.GlobalConfig, logger, sdktelemetry.EnableTelemetry)
|
||||
telemetryServer, err := telemetry.New[T](logger, sdktelemetry.EnableTelemetry, deps.GlobalConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
swaggerServer, err := swagger.New[T](logger, docs.GetSwaggerFS(), deps.GlobalConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -168,6 +176,7 @@ func InitRootCmd[T transaction.Tx](
|
||||
telemetryServer,
|
||||
restServer,
|
||||
grpcgatewayServer,
|
||||
swaggerServer,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -1,32 +1,44 @@
|
||||
[comet]
|
||||
|
||||
# min-retain-blocks defines the minimum block height offset from the current block being committed, such that all blocks past this offset are pruned from CometBFT. A value of 0 indicates that no blocks should be pruned.
|
||||
min-retain-blocks = 0
|
||||
|
||||
# halt-height contains a non-zero block height at which a node will gracefully halt and shutdown that can be used to assist upgrades and testing.
|
||||
halt-height = 0
|
||||
|
||||
# halt-time contains a non-zero minimum block time (in Unix seconds) at which a node will gracefully halt and shutdown that can be used to assist upgrades and testing.
|
||||
halt-time = 0
|
||||
|
||||
# address defines the CometBFT RPC server address to bind to.
|
||||
address = 'tcp://127.0.0.1:26658'
|
||||
|
||||
# transport defines the CometBFT RPC server transport protocol: socket, grpc
|
||||
transport = 'socket'
|
||||
|
||||
# trace enables the CometBFT RPC server to output trace information about its internal operations.
|
||||
trace = false
|
||||
|
||||
# standalone starts the application without the CometBFT node. The node should be started separately.
|
||||
standalone = false
|
||||
|
||||
# index-abci-events defines the set of events in the form {eventType}.{attributeKey}, which informs CometBFT what to index. If empty, all events will be indexed.
|
||||
index-abci-events = []
|
||||
|
||||
# disable-index-abci-events disables the ABCI event indexing done by CometBFT. Useful when relying on the SDK indexer for event indexing, but still want events to be included in FinalizeBlockResponse.
|
||||
disable-index-abci-events = false
|
||||
|
||||
# disable-abci-events disables all ABCI events. Useful when relying on the SDK indexer for event indexing.
|
||||
disable-abci-events = false
|
||||
|
||||
# mempool defines the configuration for the SDK built-in app-side mempool implementations.
|
||||
[comet.mempool]
|
||||
|
||||
# max-txs defines the maximum number of transactions that can be in the mempool. A value of 0 indicates an unbounded mempool, a negative value disables the app-side mempool.
|
||||
max-txs = -1
|
||||
|
||||
# indexer defines the configuration for the SDK built-in indexer implementation.
|
||||
[comet.indexer]
|
||||
|
||||
# Buffer size of the channels used for buffering data sent to indexer go routines.
|
||||
channel_buffer_size = 1024
|
||||
|
||||
@ -34,76 +46,133 @@ channel_buffer_size = 1024
|
||||
[comet.indexer.target]
|
||||
|
||||
[grpc]
|
||||
|
||||
# Enable defines if the gRPC server should be enabled.
|
||||
enable = true
|
||||
|
||||
# Address defines the gRPC server address to bind to.
|
||||
address = 'localhost:9090'
|
||||
|
||||
# MaxRecvMsgSize defines the max message size in bytes the server can receive.
|
||||
# The default value is 10MB.
|
||||
max-recv-msg-size = 10485760
|
||||
|
||||
# MaxSendMsgSize defines the max message size in bytes the server can send.
|
||||
# The default value is math.MaxInt32.
|
||||
max-send-msg-size = 2147483647
|
||||
|
||||
[grpc-gateway]
|
||||
# Enable defines if the gRPC-gateway should be enabled.
|
||||
|
||||
# Enable defines if the gRPC-Gateway should be enabled.
|
||||
enable = true
|
||||
# Address defines the address the gRPC-gateway server binds to.
|
||||
|
||||
# Address defines the address the gRPC-Gateway server binds to.
|
||||
address = 'localhost:1317'
|
||||
|
||||
[rest]
|
||||
|
||||
# Enable defines if the REST server should be enabled.
|
||||
enable = true
|
||||
|
||||
# Address defines the REST server address to bind to.
|
||||
address = 'localhost:8080'
|
||||
|
||||
[server]
|
||||
|
||||
# minimum-gas-prices defines the price which a validator is willing to accept for processing a transaction. A transaction's fees must meet the minimum of any denomination specified in this config (e.g. 0.25token1;0.0001token2).
|
||||
minimum-gas-prices = '0stake'
|
||||
|
||||
[store]
|
||||
|
||||
# The type of database for application and snapshots databases.
|
||||
app-db-backend = 'goleveldb'
|
||||
|
||||
[store.options]
|
||||
|
||||
# State commitment database type. Currently we support: "iavl" and "iavl-v2"
|
||||
sc-type = 'iavl'
|
||||
|
||||
# Pruning options for state commitment
|
||||
[store.options.sc-pruning-option]
|
||||
|
||||
# Number of recent heights to keep on disk.
|
||||
keep-recent = 2
|
||||
|
||||
# Height interval at which pruned heights are removed from disk.
|
||||
interval = 100
|
||||
|
||||
[store.options.iavl-config]
|
||||
|
||||
# CacheSize set the size of the iavl tree cache.
|
||||
cache-size = 100000
|
||||
cache-size = 500000
|
||||
|
||||
# If true, the tree will work like no fast storage and always not upgrade fast storage.
|
||||
skip-fast-storage-upgrade = true
|
||||
|
||||
[store.options.iavl-v2-config]
|
||||
|
||||
# CheckpointInterval set the interval of the checkpoint.
|
||||
checkpoint-interval = 0
|
||||
|
||||
# CheckpointMemory set the memory of the checkpoint.
|
||||
checkpoint-memory = 0
|
||||
|
||||
# StateStorage set the state storage.
|
||||
state-storage = false
|
||||
|
||||
# HeightFilter set the height filter.
|
||||
height-filter = 0
|
||||
|
||||
# EvictionDepth set the eviction depth.
|
||||
eviction-depth = 0
|
||||
|
||||
# PruneRatio set the prune ratio.
|
||||
prune-ratio = 0.0
|
||||
|
||||
# MinimumKeepVersions set the minimum keep versions.
|
||||
minimum-keep-versions = 0
|
||||
|
||||
[swagger]
|
||||
|
||||
# Enable enables/disables the Swagger UI server
|
||||
enable = true
|
||||
|
||||
# Address defines the server address to bind to
|
||||
address = 'localhost:8090'
|
||||
|
||||
[telemetry]
|
||||
|
||||
# Enable enables the application telemetry functionality. When enabled, an in-memory sink is also enabled by default. Operators may also enabled other sinks such as Prometheus.
|
||||
enable = true
|
||||
|
||||
# Address defines the metrics server address to bind to.
|
||||
address = 'localhost:7180'
|
||||
|
||||
# Prefixed with keys to separate services.
|
||||
service-name = ''
|
||||
|
||||
# Enable prefixing gauge values with hostname.
|
||||
enable-hostname = false
|
||||
|
||||
# Enable adding hostname to labels.
|
||||
enable-hostname-label = false
|
||||
|
||||
# Enable adding service to labels.
|
||||
enable-service-label = false
|
||||
|
||||
# PrometheusRetentionTime, when positive, enables a Prometheus metrics sink. It defines the retention duration in seconds.
|
||||
prometheus-retention-time = 0
|
||||
prometheus-retention-time = 600
|
||||
|
||||
# GlobalLabels defines a global set of name/value label tuples applied to all metrics emitted using the wrapper functions defined in telemetry package.
|
||||
# Example:
|
||||
# [["chain_id", "cosmoshub-1"]]
|
||||
global-labels = []
|
||||
|
||||
# MetricsSink defines the type of metrics backend to use. Default is in memory
|
||||
metrics-sink = ''
|
||||
|
||||
# StatsdAddr defines the address of a statsd server to send metrics to. Only utilized if MetricsSink is set to "statsd" or "dogstatsd".
|
||||
stats-addr = ''
|
||||
|
||||
# DatadogHostname defines the hostname to use when emitting metrics to Datadog. Only utilized if MetricsSink is set to "dogstatsd".
|
||||
data-dog-hostname = ''
|
||||
|
||||
Loading…
Reference in New Issue
Block a user