feat(api): Add block height to response headers (#24428)
Co-authored-by: Alex | Interchain Labs <alex@interchainlabs.io>
This commit is contained in:
parent
1e92c9c7b7
commit
f18c6ea274
@ -60,6 +60,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
|
||||
* x/distribution can now utilize an externally managed community pool. NOTE: this will make the message handlers for FundCommunityPool and CommunityPoolSpend error, as well as the query handler for CommunityPool.
|
||||
* (client) [#18101](https://github.com/cosmos/cosmos-sdk/pull/18101) Add a `keyring-default-keyname` in `client.toml` for specifying a default key name, and skip the need to use the `--from` flag when signing transactions.
|
||||
* (x/gov) [#24355](https://github.com/cosmos/cosmos-sdk/pull/24355) Allow users to set a custom CalculateVoteResultsAndVotingPower function to be used in govkeeper.Tally.
|
||||
* (api) [#24428](https://github.com/cosmos/cosmos-sdk/pull/24428) Add block height to response headers
|
||||
|
||||
### Improvements
|
||||
|
||||
|
||||
@ -11,6 +11,7 @@ import (
|
||||
|
||||
tmrpcserver "github.com/cometbft/cometbft/rpc/jsonrpc/server"
|
||||
gateway "github.com/cosmos/gogogateway"
|
||||
"github.com/golang/protobuf/proto" //nolint:staticcheck // grpc-gateway uses deprecated golang/protobuf
|
||||
"github.com/gorilla/handlers"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/grpc-ecosystem/grpc-gateway/runtime"
|
||||
@ -84,11 +85,23 @@ func New(clientCtx client.Context, logger log.Logger, grpcSrv *grpc.Server) *Ser
|
||||
// Custom header matcher for mapping request headers to
|
||||
// GRPC metadata
|
||||
runtime.WithIncomingHeaderMatcher(CustomGRPCHeaderMatcher),
|
||||
|
||||
// extension to set custom response headers
|
||||
runtime.WithForwardResponseOption(customGRPCResponseHeaders),
|
||||
),
|
||||
GRPCSrv: grpcSrv,
|
||||
}
|
||||
}
|
||||
|
||||
func customGRPCResponseHeaders(ctx context.Context, w http.ResponseWriter, _ proto.Message) error {
|
||||
if meta, ok := runtime.ServerMetadataFromContext(ctx); ok {
|
||||
if values := meta.HeaderMD.Get(grpctypes.GRPCBlockHeightHeader); len(values) == 1 {
|
||||
w.Header().Set(grpctypes.GRPCBlockHeightHeader, values[0])
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Start starts the API server. Internally, the API server leverages CometBFT's
|
||||
// JSON RPC server. Configuration options are provided via config.APIConfig
|
||||
// and are delegated to the CometBFT JSON RPC server.
|
||||
|
||||
@ -3,6 +3,9 @@ package distribution
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/cosmos/gogoproto/proto"
|
||||
"github.com/stretchr/testify/suite"
|
||||
@ -506,3 +509,60 @@ func (s *GRPCQueryTestSuite) TestQueryValidatorCommunityPoolGRPC() {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (s *GRPCQueryTestSuite) TestQueryResponseMeta() {
|
||||
val := s.network.Validators[0]
|
||||
baseURL := val.APIAddress
|
||||
startHeight, err := s.network.LatestHeight()
|
||||
s.Require().NoError(err)
|
||||
// wait 1 block to ensure state is committed
|
||||
s.Require().NoError(s.network.WaitForNextBlock())
|
||||
// when
|
||||
queryURL := fmt.Sprintf("%s/cosmos/distribution/v1beta1/validators/%s", baseURL, val.ValAddress.String())
|
||||
_, headers, err := doRequest(queryURL, map[string]string{})
|
||||
// then latest height is used
|
||||
s.Require().NoError(err)
|
||||
const heightRespHeaderKey = "X-Cosmos-Block-Height"
|
||||
s.Require().Contains(headers, heightRespHeaderKey)
|
||||
gotHeight, err := strconv.Atoi(headers[heightRespHeaderKey][0])
|
||||
s.Require().NoError(err)
|
||||
s.Assert().GreaterOrEqual(gotHeight, int(startHeight))
|
||||
|
||||
// and when called with height header
|
||||
_, headers, err = doRequest(queryURL, map[string]string{"X-Cosmos-Block-Height": strconv.Itoa(int(startHeight))})
|
||||
// then
|
||||
s.Require().NoError(err)
|
||||
s.Require().Contains(headers, heightRespHeaderKey)
|
||||
gotHeight, err = strconv.Atoi(headers[heightRespHeaderKey][0])
|
||||
s.Require().NoError(err)
|
||||
s.Assert().Equal(int(startHeight), gotHeight)
|
||||
}
|
||||
|
||||
func doRequest(url string, headers map[string]string) ([]byte, http.Header, error) {
|
||||
req, err := http.NewRequest("GET", url, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
client := &http.Client{}
|
||||
|
||||
for key, value := range headers {
|
||||
req.Header.Set(key, value)
|
||||
}
|
||||
|
||||
res, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if err = res.Body.Close(); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
fmt.Printf("headers: %v\n", res.Header)
|
||||
return body, res.Header, nil
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user