-
Notifications
You must be signed in to change notification settings - Fork 3.6k
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: wire v2 handlers #22112
base: main
Are you sure you want to change the base?
feat: wire v2 handlers #22112
Conversation
📝 Walkthrough📝 WalkthroughWalkthroughThe pull request focuses on updates to the Changes
Possibly related PRs
Suggested labels
Suggested reviewers
📜 Recent review detailsConfiguration used: .coderabbit.yml ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (2)
Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
Documentation and Community
|
go func() { | ||
s.logger.Info("Starting HTTP server", "address", s.config.Address) | ||
if err := s.httpServer.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) { | ||
s.logger.Error("Failed to start HTTP server", "error", err) | ||
} | ||
}() |
Check notice
Code scanning / CodeQL
Spawning a Go routine Note
@randygrok your pull request is missing a changelog! |
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.
Actionable comments posted: 9
🧹 Outside diff range and nitpick comments (12)
server/v2/api/rest/config.go (2)
10-10
: LGTM: CfgOption type alias is well-defined.The
CfgOption
type alias is a good use of the functional options pattern, allowing for flexible and extensible configuration.Consider adding a brief comment explaining the purpose of this type alias for better documentation:
// CfgOption defines a function type for modifying Config options type CfgOption func(*Config)
12-18
: LGTM: Config struct is well-defined and documented.The
Config
struct is well-structured with clear field names and appropriate tags for configuration parsing. The comments provide good explanations for each field.Consider reordering the fields to group related concepts together and potentially save memory through better alignment:
type Config struct { // Address defines the API server to listen on Address string `mapstructure:"address" toml:"address" comment:"Address defines the HTTP server address to bind to."` // Enable defines if the HTTP server should be enabled. Enable bool `mapstructure:"enable" toml:"enable" comment:"Enable defines if the HTTP server should be enabled."` }This ordering puts the larger
string
field first, which can lead to better memory alignment in some cases.server/v2/api/rest/server_test.go (3)
11-38
: LGTM: Well-structured table-driven tests.The use of table-driven tests is excellent for testing multiple scenarios. The test function name follows Go conventions, and the structure allows for easy addition of new test cases.
Consider adding a brief comment describing the purpose of the
TestServerConfig
function for improved readability.
17-38
: LGTM: Good coverage of default and custom configurations.The test cases effectively cover both default and custom configurations. The use of generics with
New[transaction.Tx]()
is appropriate.Consider expanding the custom configuration test case to cover more fields of the
Config
struct. This would provide more comprehensive testing of the configuration options. For example:{ name: "Custom configuration", setupFunc: func() *Config { s := New[transaction.Tx](func(config *Config) { config.Enable = false config.Address = "custom:9090" // Add more custom configurations here }) return s.Config().(*Config) }, expectedConfig: &Config{ Enable: false, Address: "custom:9090", // Add corresponding expected values here }, },
40-46
: LGTM: Good use of subtests and assertions.The test execution loop effectively uses subtests with
t.Run
, which is excellent for clear test output and selective test running. The use ofrequire.Equal
is appropriate for comparing the configuration structs.Consider adding test cases for error scenarios or invalid configurations to ensure robust error handling. For example:
{ name: "Invalid configuration", setupFunc: func() *Config { s := New[transaction.Tx](func(config *Config) { config.Address = "invalid:address" }) return s.Config().(*Config) }, expectedError: "invalid address format", },Then, modify the test execution to handle these error cases:
for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { if tc.expectedError != "" { require.PanicsWithError(t, tc.expectedError, tc.setupFunc) } else { config := tc.setupFunc() require.Equal(t, tc.expectedConfig, config) } }) }runtime/v2/services_test.go (2)
14-26
: LGTM: MockModule implementation looks good.The MockModule struct and its methods are well-implemented. They correctly use the mock package and implement the required interfaces.
Consider adding comments to explain the purpose of MockModule and its methods, especially for readers who might not be familiar with the testing strategy. For example:
// MockModule is a mock implementation of appmodulev2.AppModule // that also implements HasMsgHandlers and HasQueryHandlers interfaces. type MockModule struct { // ... rest of the code } // RegisterMsgHandlers records the call to register message handlers. func (m *MockModule) RegisterMsgHandlers(router appmodulev2.MsgRouter) { // ... rest of the code } // RegisterQueryHandlers records the call to register query handlers. func (m *MockModule) RegisterQueryHandlers(router appmodulev2.QueryRouter) { // ... rest of the code }
28-50
: LGTM: TestRegisterServices is well-structured and covers the main functionality.The test function is well-implemented, following a clear setup-execution-assertion pattern. It correctly uses mock expectations and assertions to verify the behavior of the RegisterServices method.
To improve test coverage, consider adding the following:
A negative test case where RegisterServices returns an error. This could be achieved by making the mock module return an error and asserting that it's propagated correctly.
Test with multiple modules to ensure RegisterServices works correctly with more than one module.
Here's a sketch of how you might implement these suggestions:
func TestRegisterServices(t *testing.T) { t.Run("successful registration", func(t *testing.T) { // ... existing test code ... }) t.Run("error propagation", func(t *testing.T) { mockModule := new(MockModule) app := &App[transaction.Tx]{ msgRouterBuilder: stf.NewMsgRouterBuilder(), queryRouterBuilder: stf.NewMsgRouterBuilder(), } mm := &MM[transaction.Tx]{ modules: map[string]appmodulev2.AppModule{ "mock": mockModule, }, } expectedError := errors.New("registration error") mockModule.On("RegisterMsgHandlers", app.msgRouterBuilder).Return(expectedError) err := mm.RegisterServices(app) assert.Error(t, err) assert.Equal(t, expectedError, err) mockModule.AssertExpectations(t) }) t.Run("multiple modules", func(t *testing.T) { mockModule1 := new(MockModule) mockModule2 := new(MockModule) app := &App[transaction.Tx]{ msgRouterBuilder: stf.NewMsgRouterBuilder(), queryRouterBuilder: stf.NewMsgRouterBuilder(), } mm := &MM[transaction.Tx]{ modules: map[string]appmodulev2.AppModule{ "mock1": mockModule1, "mock2": mockModule2, }, } mockModule1.On("RegisterMsgHandlers", app.msgRouterBuilder).Once() mockModule1.On("RegisterQueryHandlers", app.queryRouterBuilder).Once() mockModule2.On("RegisterMsgHandlers", app.msgRouterBuilder).Once() mockModule2.On("RegisterQueryHandlers", app.queryRouterBuilder).Once() err := mm.RegisterServices(app) assert.NoError(t, err) mockModule1.AssertExpectations(t) mockModule2.AssertExpectations(t) }) }These additional test cases will help ensure that RegisterServices behaves correctly in various scenarios.
server/v2/api/rest/README (3)
1-7
: Enhance the general description for clarity.The introduction and general description provide a good overview of the API. However, to improve clarity, consider specifying that all endpoints use the HTTP POST method in the general description.
Suggested change for line 7:
- The service allows querying the blockchain using any type of Protobuf message available in the Cosmos SDK application through HTTP `POST` requests. Each endpoint corresponds to a Cosmos SDK protocol message (`proto`), and responses are returned in JSON format. + The service allows querying the blockchain using any type of Protobuf message available in the Cosmos SDK application. All endpoints use HTTP `POST` requests. Each endpoint corresponds to a Cosmos SDK protocol message (`proto`), and responses are returned in JSON format.
9-58
: Excellent example, consider adding more details to the response explanation.The QueryBalanceRequest example is well-structured and provides comprehensive information. The use of code blocks for JSON examples enhances readability.
To further improve the documentation, consider expanding the explanation of the response. For example, you could add:
The response shows the balance of the specified token for the given account. + The `amount` field represents the token balance as a string to preserve precision for large numbers. Developers should handle this as a big integer or decimal in their applications.
60-73
: Great curl example, consider adding more context.The curl example is clear and provides a practical way for users to interact with the API.
To make this section even more helpful, consider:
- Adding a brief explanation of what the curl command does.
- Mentioning other common tools that can be used (e.g., Postman, httpie).
For example:
+ This command sends a POST request to the QueryBalanceRequest endpoint with the specified JSON payload. + + ### 2. Using other tools + + You can also use other HTTP client tools like Postman or httpie to interact with the API. The key is to ensure you're sending a POST request with the correct Content-Type header and JSON payload.server/v2/api/rest/handler.go (2)
63-63
: Check for errors when encoding the JSON responseThe
Encode
method may return an error, but the current implementation does not check for it. This could lead to unnoticed failures in encoding the response.Add error handling for the JSON encoding:
- json.NewEncoder(w).Encode(query) + err = json.NewEncoder(w).Encode(query) + if err != nil { + http.Error(w, "Error encoding response", http.StatusInternalServerError) + return + }
57-57
: Avoid using magic numbers; define a constant for clarityUsing a literal
0
inh.appManager.Query(r.Context(), 0, msg)
reduces readability. It's unclear what the0
represents.Define a meaningful constant or add a comment to explain the significance of
0
:const defaultHeight = 0 // ... query, err := h.appManager.Query(r.Context(), defaultHeight, msg)
📜 Review details
Configuration used: .coderabbit.yml
Review profile: CHILL
⛔ Files ignored due to path filters (1)
runtime/v2/go.sum
is excluded by!**/*.sum
📒 Files selected for processing (9)
- runtime/v2/go.mod (2 hunks)
- runtime/v2/services_test.go (1 hunks)
- server/v2/api/rest/README (1 hunks)
- server/v2/api/rest/config.go (1 hunks)
- server/v2/api/rest/handler.go (1 hunks)
- server/v2/api/rest/server.go (1 hunks)
- server/v2/api/rest/server_test.go (1 hunks)
- server/v2/go.mod (1 hunks)
- simapp/v2/simdv2/cmd/commands.go (2 hunks)
🧰 Additional context used
📓 Path-based instructions (6)
runtime/v2/services_test.go (2)
Pattern
**/*.go
: Review the Golang code for conformity with the Uber Golang style guide, highlighting any deviations.
Pattern
**/*_test.go
: "Assess the unit test code assessing sufficient code coverage for the changes associated in the pull request"server/v2/api/rest/config.go (1)
Pattern
**/*.go
: Review the Golang code for conformity with the Uber Golang style guide, highlighting any deviations.server/v2/api/rest/handler.go (1)
Pattern
**/*.go
: Review the Golang code for conformity with the Uber Golang style guide, highlighting any deviations.server/v2/api/rest/server.go (1)
Pattern
**/*.go
: Review the Golang code for conformity with the Uber Golang style guide, highlighting any deviations.server/v2/api/rest/server_test.go (2)
Pattern
**/*.go
: Review the Golang code for conformity with the Uber Golang style guide, highlighting any deviations.
Pattern
**/*_test.go
: "Assess the unit test code assessing sufficient code coverage for the changes associated in the pull request"simapp/v2/simdv2/cmd/commands.go (1)
Pattern
**/*.go
: Review the Golang code for conformity with the Uber Golang style guide, highlighting any deviations.
🔇 Additional comments (10)
server/v2/api/rest/config.go (1)
3-8
: LGTM: DefaultConfig function is well-implemented.The
DefaultConfig
function provides sensible default values for the HTTP server configuration. It follows Go conventions and returns a pointer to aConfig
struct, which is a common pattern for configuration objects.server/v2/api/rest/server_test.go (2)
1-9
: LGTM: Package declaration and imports are appropriate.The package name 'rest' is suitable for a REST API server test. The imports are relevant and include the necessary testing utilities. The use of
testify/require
is a good practice for writing clear and concise tests.
1-46
: Overall, well-structured and effective test file.This test file for the server configuration is well-written and follows good Go testing practices. It effectively uses table-driven tests and subtests, which enhances readability and maintainability. The test cases cover both default and custom configurations, providing a good foundation for ensuring the correct behavior of the server configuration.
To further improve the test suite, consider:
- Expanding the custom configuration test to cover more fields.
- Adding test cases for error scenarios or invalid configurations.
- Adding a brief comment describing the purpose of the
TestServerConfig
function.These enhancements would make the tests more comprehensive and robust, ensuring better coverage of potential edge cases and error handling.
runtime/v2/services_test.go (1)
1-12
: LGTM: Package declaration and imports are well-organized.The package declaration is correct, and the imports are appropriately organized. All imported packages seem relevant to the test file's purpose.
runtime/v2/go.mod (2)
74-74
: Verify security implications of new indirect dependencies.The addition of
github.com/stretchr/objx v0.5.2
as an indirect dependency, along with several other indirect dependency updates, is noted. While these changes are likely due to updates in direct dependencies, it's important to ensure they don't introduce any security vulnerabilities.Please run the following command to check for any known vulnerabilities in the updated dependencies:
#!/bin/bash # Run go list to get all dependencies and pipe to govulncheck go list -json -m all | govulncheck -json -If
govulncheck
is not available, consider installing it or using an alternative vulnerability scanning tool suitable for Go projects.
24-24
: LGTM: Addition of testify package.The addition of
github.com/stretchr/testify v1.9.0
as a direct dependency is appropriate. This package is widely used for writing unit tests in Go projects.To ensure this dependency is being utilized, please run the following command:
server/v2/go.mod (1)
24-24
: Consider updating gogo/protobuf and verify its necessity as a direct dependency.The addition of
github.com/gogo/protobuf v1.3.2
as a direct dependency is noted. This change aligns with the PR objectives for wiring v2 handlers. However, please consider the following points:
- Version 1.3.2 is not the latest available. Check if a newer version can be used to ensure you have the latest features and security updates.
- Verify if this dependency needs to be direct. If it's not used explicitly in the codebase, it might be better to keep it as an indirect dependency.
To check for usage and available versions, you can run:
simapp/v2/simdv2/cmd/commands.go (3)
18-18
: LGTM: New import statement is correctly placed and used.The new import for the
rest
package is properly grouped with other imports from the same root package and follows the alphabetical order within its group. This adheres to the Uber Golang style guide.
87-87
: LGTM: REST API handler added correctly.The addition of
rest.New[T]()
to theserverv2.AddCommands
call is consistent with the existing pattern and correctly integrates the REST API functionality into the command initialization process. The use of generics with[T]
is also consistent with other function calls in the same block.
Line range hint
1-238
: Summary: REST API integration added successfully.The changes in this file successfully integrate REST API functionality into the command initialization process of the application. The new import and the addition of
rest.New[T]()
to theserverv2.AddCommands
call are minimal, well-placed, and consistent with the existing code structure. These changes enhance the application's capabilities by allowing it to handle RESTful requests alongside existing functionalities.
# Cosmos SDK REST API | ||
|
||
This document describes how to use a service that exposes endpoints based on Cosmos SDK Protobuf message types. Each endpoint responds with data in JSON format. | ||
|
||
## General Description | ||
|
||
The service allows querying the blockchain using any type of Protobuf message available in the Cosmos SDK application through HTTP `POST` requests. Each endpoint corresponds to a Cosmos SDK protocol message (`proto`), and responses are returned in JSON format. | ||
|
||
## Example | ||
|
||
### 1. `QueryBalanceRequest` | ||
|
||
This endpoint allows querying the balance of an account given an address and a token denomination. | ||
|
||
- **URL:** `localhost:8080/cosmos.bank.v2.QueryBalanceRequest` | ||
|
||
- **Method:** `POST` | ||
|
||
- **Headers:** | ||
|
||
- `Content-Type: application/json` | ||
|
||
- **Body (JSON):** | ||
|
||
```json | ||
{ | ||
"address": "<ACCOUNT_ADDRESS>", | ||
"denom": "<TOKEN_DENOMINATION>" | ||
} | ||
``` | ||
|
||
- `address`: Account address on the Cosmos network. | ||
- `denom`: Token denomination (e.g., `stake`). | ||
|
||
- **Request Example:** | ||
|
||
``` | ||
POST localhost:8080/cosmos.bank.v2.QueryBalanceRequest | ||
Content-Type: application/json | ||
|
||
{ | ||
"address": "cosmos16tms8tax3ha9exdu7x3maxrvall07yum3rdcu0", | ||
"denom": "stake" | ||
} | ||
``` | ||
|
||
- **Response Example (JSON):** | ||
|
||
```json | ||
{ | ||
"balance": { | ||
"denom": "stake", | ||
"amount": "1000000" | ||
} | ||
} | ||
``` | ||
|
||
The response shows the balance of the specified token for the given account. | ||
|
||
## Using Tools | ||
|
||
### 1. Using `curl` | ||
|
||
To make a request using `curl`, you can run the following command: | ||
|
||
```bash | ||
curl -X POST localhost:8080/cosmos.bank.v2.QueryBalanceRequest \ | ||
-H "Content-Type: application/json" \ | ||
-d '{ | ||
"address": "cosmos16tms8tax3ha9exdu7x3maxrvall07yum3rdcu0", | ||
"denom": "stake" | ||
}' | ||
``` |
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.
🛠️ Refactor suggestion
Consider adding sections on error handling and authentication.
The README provides a solid introduction to the Cosmos SDK REST API with clear examples and usage instructions. To make it even more comprehensive, consider adding the following sections:
- Error Handling: Describe common error responses and their meanings.
- Authentication: If the API requires authentication, explain the process and provide examples.
- Rate Limiting: If applicable, mention any rate limiting policies.
- Additional Endpoints: Briefly list or link to documentation for other available endpoints.
Here's a suggested structure for these additional sections:
## Error Handling
Describe common error responses, their status codes, and how to interpret them.
## Authentication
If the API requires authentication, explain the process here and provide examples.
## Rate Limiting
If applicable, describe any rate limiting policies and how to handle rate limit errors.
## Additional Endpoints
For a complete list of available endpoints and their documentation, please refer to [link to full API documentation].
return | ||
} | ||
|
||
msg := reflect.New(requestType.Elem()).Interface().(gogoproto.Message) |
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.
🛠️ Refactor suggestion
Minimize the use of reflection for better performance and safety
Reflection can introduce performance overhead and potential runtime errors. According to the Uber Go Style Guide, reflection should be used carefully.
Consider alternative approaches to create new message instances without reflection. For example, maintain a registry or map of constructor functions keyed by message type.
contentType := r.Header.Get("Content-Type") | ||
if contentType != ContentTypeJSON { | ||
contentType = ContentTypeJSON | ||
} |
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.
Validate Content-Type
header instead of defaulting
Currently, if the Content-Type
is not application/json
, the handler defaults it to application/json
. This may lead to unexpected behavior if the client sends data with an incorrect content type.
Consider validating the Content-Type
and returning a 415 Unsupported Media Type
error if it's not application/json
:
contentType := r.Header.Get("Content-Type")
if contentType != ContentTypeJSON {
- contentType = ContentTypeJSON
+ http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
+ return
}
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
contentType := r.Header.Get("Content-Type") | |
if contentType != ContentTypeJSON { | |
contentType = ContentTypeJSON | |
} | |
contentType := r.Header.Get("Content-Type") | |
if contentType != ContentTypeJSON { | |
http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType) | |
return | |
} |
if err != nil { | ||
http.Error(w, "Error parsing body", http.StatusBadRequest) | ||
fmt.Fprintf(w, "Error parsing body: %v\n", err) | ||
return | ||
} |
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.
Consolidate error handling when parsing the request body
Using both http.Error
and fmt.Fprintf
to write error responses can result in malformed HTTP responses because headers may have already been sent. Instead, send a single, well-formatted error response.
Modify the error handling to return a JSON response with error details:
if err != nil {
- http.Error(w, "Error parsing body", http.StatusBadRequest)
- fmt.Fprintf(w, "Error parsing body: %v\n", err)
+ w.Header().Set("Content-Type", ContentTypeJSON)
+ w.WriteHeader(http.StatusBadRequest)
+ json.NewEncoder(w).Encode(map[string]string{
+ "error": fmt.Sprintf("Error parsing body: %v", err),
+ })
return
}
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
if err != nil { | |
http.Error(w, "Error parsing body", http.StatusBadRequest) | |
fmt.Fprintf(w, "Error parsing body: %v\n", err) | |
return | |
} | |
if err != nil { | |
w.Header().Set("Content-Type", ContentTypeJSON) | |
w.WriteHeader(http.StatusBadRequest) | |
json.NewEncoder(w).Encode(map[string]string{ | |
"error": fmt.Sprintf("Error parsing body: %v", err), | |
}) | |
return | |
} |
gogoproto "github.com/cosmos/gogoproto/proto" | ||
"github.com/gogo/protobuf/jsonpb" |
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.
Replace deprecated jsonpb
with protojson
The package github.com/gogo/protobuf/jsonpb
is deprecated. To ensure better maintainability and compatibility, consider using google.golang.org/protobuf/encoding/protojson
instead.
Apply the following changes:
import (
...
- "github.com/gogo/protobuf/jsonpb"
+ "google.golang.org/protobuf/encoding/protojson"
...
)
Update the usage accordingly:
- err := jsonpb.Unmarshal(r.Body, msg)
+ err := protojson.Unmarshal(r.Body, msg)
Committable suggestion was skipped due to low confidence.
} | ||
|
||
func (s *Server[T]) Config() any { | ||
if s.config == nil || s.config == (&Config{}) { |
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.
Correct the comparison in the Config()
method
In line 78, the comparison s.config == (&Config{})
is comparing the pointer s.config
to a new pointer &Config{}
. This will always evaluate to false
because they point to different addresses. Instead, you should compare the value that s.config
points to with an empty Config
struct to check if it is uninitialized.
Apply this diff to fix the comparison:
func (s *Server[T]) Config() any {
- if s.config == nil || s.config == (&Config{}) {
+ if s.config == nil || *s.config == (Config{}) {
cfg := DefaultConfig()
for _, opt := range s.cfgOptions {
opt(cfg)
}
return cfg
}
return s.config
}
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
if s.config == nil || s.config == (&Config{}) { | |
if s.config == nil || *s.config == (Config{}) { |
func (s *Server[T]) Start(ctx context.Context) error { | ||
s.httpServer = &http.Server{ | ||
Addr: s.config.Address, | ||
Handler: s.router, | ||
} |
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.
Check if the server is enabled before starting
Currently, the Start()
method starts the HTTP server without checking whether the server is enabled in the configuration. This could lead to the server running when it shouldn't be.
Apply this diff to add the enable check:
func (s *Server[T]) Start(ctx context.Context) error {
+ if !s.config.Enable {
+ s.logger.Info("HTTP server is disabled")
+ return nil
+ }
s.httpServer = &http.Server{
Addr: s.config.Address,
Handler: s.router,
}
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
func (s *Server[T]) Start(ctx context.Context) error { | |
s.httpServer = &http.Server{ | |
Addr: s.config.Address, | |
Handler: s.router, | |
} | |
func (s *Server[T]) Start(ctx context.Context) error { | |
if !s.config.Enable { | |
s.logger.Info("HTTP server is disabled") | |
return nil | |
} | |
s.httpServer = &http.Server{ | |
Addr: s.config.Address, | |
Handler: s.router, | |
} |
if err := s.httpServer.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) { | ||
s.logger.Error("Failed to start HTTP server", "error", err) | ||
} |
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.
Notify the caller if the HTTP server fails to start
In the goroutine, if ListenAndServe()
returns an error other than http.ErrServerClosed
, the error is only logged but not returned to the caller. This means the caller has no way of knowing that the server failed to start.
Consider modifying the Start()
method to capture the error and return it to the caller. Here's how you might adjust the code:
func (s *Server[T]) Start(ctx context.Context) error {
+ errChan := make(chan error, 1)
s.httpServer = &http.Server{
Addr: s.config.Address,
Handler: s.router,
}
go func() {
s.logger.Info("Starting HTTP server", "address", s.config.Address)
if err := s.httpServer.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
s.logger.Error("Failed to start HTTP server", "error", err)
+ errChan <- err
} else {
+ errChan <- nil
}
}()
+ if err := <-errChan; err != nil {
+ return err
+ }
return nil
}
This way, the Start()
method will wait for the server to start and report any errors back to the caller.
Committable suggestion was skipped due to low confidence.
func (s *Server[T]) Init(appI serverv2.AppI[T], cfg map[string]any, logger log.Logger) error { | ||
s.logger = logger.With(log.ModuleKey, s.Name()) | ||
|
||
s.config = s.Config().(*Config) | ||
|
||
var appManager *appmanager.AppManager[T] | ||
appManager = appI.GetAppManager() | ||
|
||
s.router = http.NewServeMux() | ||
s.router.Handle("/", NewDefaultHandler(appManager)) | ||
|
||
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.
Ensure the configuration is initialized properly in Init()
In the Init()
method, s.config
is set using s.Config().(*Config)
. However, if s.Config()
returns nil
, this type assertion could cause a panic. Additionally, since s.Config()
may create a new default configuration, it's important to ensure that s.config
is correctly initialized before it's used elsewhere.
Consider initializing s.config
directly if it's nil
:
func (s *Server[T]) Init(appI serverv2.AppI[T], cfg map[string]any, logger log.Logger) error {
s.logger = logger.With(log.ModuleKey, s.Name())
- s.config = s.Config().(*Config)
+ if s.config == nil {
+ s.config = DefaultConfig()
+ }
var appManager *appmanager.AppManager[T]
appManager = appI.GetAppManager()
s.router = http.NewServeMux()
s.router.Handle("/", NewDefaultHandler(appManager))
return nil
}
This ensures that s.config
is not nil
before proceeding.
Committable suggestion was skipped due to low confidence.
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.
nit, rename to README.md
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.
nice! left some comments
} | ||
} | ||
|
||
type CfgOption func(*Config) |
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.
There's no option defined, so let's delete this.
|
||
// Config defines configuration for the HTTP server. | ||
type Config struct { | ||
// Enable defines if the HTTP server should be enabled. |
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.
can we say rest server instead of http server? so it matches the component name.
func (s *Server[T]) Init(appI serverv2.AppI[T], cfg map[string]any, logger log.Logger) error { | ||
s.logger = logger.With(log.ModuleKey, s.Name()) | ||
|
||
s.config = s.Config().(*Config) |
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.
we need the same config unmarshalling than with other servers here.
} | ||
|
||
func (s *Server[T]) Start(ctx context.Context) error { | ||
s.httpServer = &http.Server{ |
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.
We need to add the enable check like we do in other servers here.
Handler: s.router, | ||
} | ||
|
||
go func() { |
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.
no need to be in a go routine, we can be blocking, server components are already running in a go routine
} | ||
|
||
func (s *Server[T]) Config() any { | ||
if s.config == nil || s.config == (&Config{}) { |
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.
if s.config == nil || s.config == (&Config{}) { | |
if s.config == nil || s.config.Address == "" { |
bot is right
Description
Closes: #XXXX
Author Checklist
All items are required. Please add a note to the item if the item is not applicable and
please add links to any relevant follow up issues.
I have...
!
in the type prefix if API or client breaking changeCHANGELOG.md
Reviewers Checklist
All items are required. Please add a note if the item is not applicable and please add
your handle next to the items reviewed if you only reviewed selected items.
Please see Pull Request Reviewer section in the contributing guide for more information on how to review a pull request.
I have...
Summary by CodeRabbit
New Features
QueryBalanceRequest
endpoint.Bug Fixes
Documentation
Tests