Skip to content
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

Open
wants to merge 17 commits into
base: main
Choose a base branch
from
Open

feat: wire v2 handlers #22112

wants to merge 17 commits into from

Conversation

randygrok
Copy link
Collaborator

@randygrok randygrok commented Oct 3, 2024

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...

  • included the correct type prefix in the PR title, you can find examples of the prefixes below:
  • confirmed ! in the type prefix if API or client breaking change
  • targeted the correct branch (see PR Targeting)
  • provided a link to the relevant issue or specification
  • reviewed "Files changed" and left comments if necessary
  • included the necessary unit and integration tests
  • added a changelog entry to CHANGELOG.md
  • updated the relevant documentation or specification, including comments for documenting Go code
  • confirmed all CI checks have passed

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...

  • confirmed the correct type prefix in the PR title
  • confirmed all author checklist items have been addressed
  • reviewed state machine logic, API design and naming, documentation is accurate, tests and test coverage

Summary by CodeRabbit

  • New Features

    • Introduced a REST API for querying blockchain data, including a QueryBalanceRequest endpoint.
    • Added support for handling RESTful requests in the application command initialization.
    • Updated dependencies to enhance module functionality and performance.
  • Bug Fixes

    • Enhanced test coverage for service registration to ensure proper module integration.
  • Documentation

    • Added a README file detailing the usage of the Cosmos SDK REST API.
  • Tests

    • Implemented unit tests for server configuration functionality.

Copy link
Contributor

coderabbitai bot commented Oct 3, 2024

📝 Walkthrough
📝 Walkthrough

Walkthrough

The pull request focuses on updates to the go.mod files for the runtime/v2 and server/v2 modules, specifically managing dependencies. In runtime/v2/go.mod, a direct dependency on github.com/stretchr/testify v1.9.0 is added, while an indirect dependency on the same package is removed and replaced with a new indirect dependency on github.com/stretchr/objx v0.5.2. In server/v2/go.mod, a new direct requirement for github.com/gogo/protobuf v1.3.2 is added, and the previous indirect requirement for this package is removed.

Changes

File Change Summary
runtime/v2/go.mod Added github.com/stretchr/testify v1.9.0, removed indirect dependency on github.com/stretchr/testify v1.9.0, added github.com/stretchr/objx v0.5.2 (indirect).
server/v2/go.mod Added github.com/gogo/protobuf v1.3.2, removed indirect requirement for github.com/gogo/protobuf v1.3.2.

Possibly related PRs

Suggested labels

C:x/staking, C:CLI, C:x/gov, C:x/slashing, C:x/distribution, C:x/mint, C:x/bank, C:x/params, C:x/evidence, C:x/upgrade, C:x/feegrant, C:x/authz, C:x/group, C:x/nft, C:x/consensus, C:x/circuit, C:x/accounts, C:x/protocolpool, C:x/epochs, C:x/accounts/lockup, C:x/accounts/multisig, C:server/v2 stf, C:server/v2 cometbft, C:x/accounts/base, C:core/testing

Suggested reviewers

  • testinginprod
  • kocubinski
  • aaronc
  • julienrbrt
  • hieuvubk

📜 Recent review details

Configuration used: .coderabbit.yml
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between ee8d0a4 and 40b5bae.

⛔ Files ignored due to path filters (1)
  • runtime/v2/go.sum is excluded by !**/*.sum
📒 Files selected for processing (2)
  • runtime/v2/go.mod (2 hunks)
  • server/v2/go.mod (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • runtime/v2/go.mod
  • server/v2/go.mod

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?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

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)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

server/v2/api/rest/handler.go Fixed Show fixed Hide fixed
Comment on lines +61 to +66
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

Spawning a Go routine may be a possible source of non-determinism
"encoding/json"
"fmt"
"net/http"
"reflect"

Check notice

Code scanning / CodeQL

Sensitive package import Note

Certain system packages contain functions which may be a possible source of non-determinism
@randygrok randygrok marked this pull request as ready for review October 7, 2024 16:37
@randygrok randygrok requested review from a team, kocubinski and hieuvubk as code owners October 7, 2024 16:37
Copy link
Contributor

github-actions bot commented Oct 7, 2024

@randygrok your pull request is missing a changelog!

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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 of require.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:

  1. 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.

  2. 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:

  1. Adding a brief explanation of what the curl command does.
  2. 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 response

The 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 clarity

Using a literal 0 in h.appManager.Query(r.Context(), 0, msg) reduces readability. It's unclear what the 0 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

📥 Commits

Files that changed from the base of the PR and between 46b01ba and ee8d0a4.

⛔ 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 a Config 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:

  1. Expanding the custom configuration test to cover more fields.
  2. Adding test cases for error scenarios or invalid configurations.
  3. 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:

  1. 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.
  2. 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 the serverv2.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 the serverv2.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.

Comment on lines +1 to +73
# 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"
}'
```
Copy link
Contributor

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:

  1. Error Handling: Describe common error responses and their meanings.
  2. Authentication: If the API requires authentication, explain the process and provide examples.
  3. Rate Limiting: If applicable, mention any rate limiting policies.
  4. 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)
Copy link
Contributor

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.

Comment on lines +37 to +40
contentType := r.Header.Get("Content-Type")
if contentType != ContentTypeJSON {
contentType = ContentTypeJSON
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

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.

Suggested change
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
}

Comment on lines +51 to +55
if err != nil {
http.Error(w, "Error parsing body", http.StatusBadRequest)
fmt.Fprintf(w, "Error parsing body: %v\n", err)
return
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

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.

Suggested change
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
}

Comment on lines +10 to +11
gogoproto "github.com/cosmos/gogoproto/proto"
"github.com/gogo/protobuf/jsonpb"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

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{}) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

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.

Suggested change
if s.config == nil || s.config == (&Config{}) {
if s.config == nil || *s.config == (Config{}) {

Comment on lines +51 to +55
func (s *Server[T]) Start(ctx context.Context) error {
s.httpServer = &http.Server{
Addr: s.config.Address,
Handler: s.router,
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

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.

Suggested change
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,
}

Comment on lines +59 to +61
if err := s.httpServer.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
s.logger.Error("Failed to start HTTP server", "error", err)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

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.

Comment on lines +37 to +49
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
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

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.

Copy link
Member

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

Copy link
Member

@julienrbrt julienrbrt left a 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)
Copy link
Member

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.
Copy link
Member

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)
Copy link
Member

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{
Copy link
Member

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() {
Copy link
Member

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{}) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if s.config == nil || s.config == (&Config{}) {
if s.config == nil || s.config.Address == "" {

bot is right

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C:server/v2 api C:server/v2 Issues related to server/v2
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants