Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ For more details, go [here](https://docs.multiversx.com/sdk-and-tools/proxy/).
- `/v1.0/network/delegated-info` (GET) --> returns the list of delegated values
- `/v1.0/network/enable-epochs` (GET) --> returns the activation epochs metric
- `/v1.0/network/enable-epochs-v2` (GET) --> returns the newer version of activation epochs
- `/v1.0/network/enable-rounds` (GET) --> returns the activation rounds metric
### node

- `/v1.0/node/heartbeatstatus` (GET) --> returns the heartbeat data from an observer from any shard. Has a cache to avoid many requests
Expand Down
11 changes: 11 additions & 0 deletions api/groups/baseNetworkGroup.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ func NewNetworkGroup(facadeHandler data.FacadeHandler) (*networkGroup, error) {
{Path: "/esdt/supply/:token", Handler: ng.getESDTSupply, Method: http.MethodGet},
{Path: "/enable-epochs", Handler: ng.getEnableEpochs, Method: http.MethodGet},
{Path: "/enable-epochs-v2", Handler: ng.getEnableEpochsV2, Method: http.MethodGet},
{Path: "/enable-rounds", Handler: ng.getEnableRounds, Method: http.MethodGet},
{Path: "/direct-staked-info", Handler: ng.getDirectStakedInfo, Method: http.MethodGet},
{Path: "/delegated-info", Handler: ng.getDelegatedInfo, Method: http.MethodGet},
{Path: "/ratings", Handler: ng.getRatingsConfig, Method: http.MethodGet},
Expand Down Expand Up @@ -155,6 +156,16 @@ func (group *networkGroup) getEnableEpochsV2(c *gin.Context) {
c.JSON(http.StatusOK, enableEpochsMetrics)
}

func (group *networkGroup) getEnableRounds(c *gin.Context) {
enableRoundsMetrics, err := group.facade.GetEnableRoundsMetrics()
if err != nil {
shared.RespondWith(c, http.StatusInternalServerError, nil, err.Error(), data.ReturnCodeInternalError)
return
}

c.JSON(http.StatusOK, enableRoundsMetrics)
}

func (group *networkGroup) getESDTSupply(c *gin.Context) {
tokenIdentifier := c.Param("token")
if tokenIdentifier == "" {
Expand Down
75 changes: 75 additions & 0 deletions api/groups/baseNetworkGroup_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -527,6 +527,81 @@ func TestGetEnableEpochsMetricsV2_OkRequestShouldWork(t *testing.T) {
assert.Equal(t, value, res)
}

func TestGetEnableRoundsMetrics_FacadeErrShouldErr(t *testing.T) {
t.Parallel()

expectedErr := errors.New("expected err")
facade := &mock.FacadeStub{
GetEnableRoundsMetricsHandler: func() (*data.GenericAPIResponse, error) {
return nil, expectedErr
},
}
networkGroup, err := groups.NewNetworkGroup(facade)
require.NoError(t, err)
ws := startProxyServer(networkGroup, networkPath)

req, _ := http.NewRequest("GET", "/network/enable-rounds", nil)
resp := httptest.NewRecorder()
ws.ServeHTTP(resp, req)
assert.Equal(t, http.StatusInternalServerError, resp.Code)

var result metricsResponse
loadResponse(resp.Body, &result)

assert.Equal(t, expectedErr.Error(), result.Error)
}

func TestGetEnableRoundsMetrics_BadRequestShouldErr(t *testing.T) {
t.Parallel()

facade := &mock.FacadeStub{
GetEnableRoundsMetricsHandler: func() (*data.GenericAPIResponse, error) {
return nil, errors.New("bad request")
},
}
networkGroup, err := groups.NewNetworkGroup(facade)
require.NoError(t, err)
ws := startProxyServer(networkGroup, networkPath)

req, _ := http.NewRequest("GET", "/network/enable-rounds", nil)
resp := httptest.NewRecorder()
ws.ServeHTTP(resp, req)

assert.Equal(t, http.StatusInternalServerError, resp.Code)
}

func TestGetEnableRoundsMetrics_OkRequestShouldWork(t *testing.T) {
t.Parallel()

key := "SupernovaEnableRound"
value := float64(100)
facade := &mock.FacadeStub{
GetEnableRoundsMetricsHandler: func() (*data.GenericAPIResponse, error) {
return &data.GenericAPIResponse{
Data: map[string]interface{}{
key: value,
},
Error: "",
}, nil
},
}
networkGroup, err := groups.NewNetworkGroup(facade)
require.NoError(t, err)
ws := startProxyServer(networkGroup, networkPath)

req, _ := http.NewRequest("GET", "/network/enable-rounds", nil)
resp := httptest.NewRecorder()
ws.ServeHTTP(resp, req)
assert.Equal(t, http.StatusOK, resp.Code)

var result metricsResponse
loadResponse(resp.Body, &result)

res, ok := result.Data[key]
assert.True(t, ok)
assert.Equal(t, value, res)
}

func TestGetRatingsConfig_ShouldFail(t *testing.T) {
t.Parallel()

Expand Down
1 change: 1 addition & 0 deletions api/groups/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ type NetworkFacadeHandler interface {
GetDelegatedInfo() (*data.GenericAPIResponse, error)
GetEnableEpochsMetrics() (*data.GenericAPIResponse, error)
GetEnableEpochsMetricsV2() (*data.GenericAPIResponse, error)
GetEnableRoundsMetrics() (*data.GenericAPIResponse, error)
GetESDTSupply(token string) (*data.ESDTSupplyResponse, error)
GetRatingsConfig() (*data.GenericAPIResponse, error)
GetGenesisNodesPubKeys() (*data.GenericAPIResponse, error)
Expand Down
6 changes: 6 additions & 0 deletions api/mock/facadeStub.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ type FacadeStub struct {
GetAllIssuedESDTsHandler func(tokenType string) (*data.GenericAPIResponse, error)
GetEnableEpochsMetricsHandler func() (*data.GenericAPIResponse, error)
GetEnableEpochsMetricsV2Handler func() (*data.GenericAPIResponse, error)
GetEnableRoundsMetricsHandler func() (*data.GenericAPIResponse, error)
GetEconomicsDataMetricsHandler func() (*data.GenericAPIResponse, error)
GetDirectStakedInfoCalled func() (*data.GenericAPIResponse, error)
GetDelegatedInfoCalled func() (*data.GenericAPIResponse, error)
Expand Down Expand Up @@ -241,6 +242,11 @@ func (f *FacadeStub) GetEnableEpochsMetricsV2() (*data.GenericAPIResponse, error
return f.GetEnableEpochsMetricsV2Handler()
}

// GetEnableRoundsMetrics -
func (f *FacadeStub) GetEnableRoundsMetrics() (*data.GenericAPIResponse, error) {
return f.GetEnableRoundsMetricsHandler()
}

// GetRatingsConfig -
func (f *FacadeStub) GetRatingsConfig() (*data.GenericAPIResponse, error) {
return f.GetRatingsConfigCalled()
Expand Down
1 change: 1 addition & 0 deletions cmd/proxy/config/apiConfig/v1_0.toml
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ Routes = [
{ Name = "/delegated-info", Open = true, Secured = true, RateLimit = 0 },
{ Name = "/enable-epochs", Open = true, Secured = false, RateLimit = 0 },
{ Name = "/enable-epochs-v2", Open = true, Secured = false, RateLimit = 0 },
{ Name = "/enable-rounds", Open = true, Secured = false, RateLimit = 0 },
{ Name = "/ratings", Open = true, Secured = false, RateLimit = 0 },
{ Name = "/genesis-nodes", Open = true, Secured = false, RateLimit = 0 },
{ Name = "/gas-configs", Open = true, Secured = false, RateLimit = 0 },
Expand Down
1 change: 1 addition & 0 deletions cmd/proxy/config/apiConfig/v_next.toml
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ Routes = [
{ Name = "/delegated-info", Open = true, Secured = true, RateLimit = 0 },
{ Name = "/enable-epochs", Open = true, Secured = false, RateLimit = 0 },
{ Name = "/enable-epochs-v2", Open = true, Secured = false, RateLimit = 0 },
{ Name = "/enable-rounds", Open = true, Secured = false, RateLimit = 0 },
{ Name = "/ratings", Open = true, Secured = false, RateLimit = 0 },
{ Name = "/genesis-nodes", Open = true, Secured = false, RateLimit = 0 },
{ Name = "/gas-configs", Open = true, Secured = false, RateLimit = 0 },
Expand Down
20 changes: 20 additions & 0 deletions cmd/proxy/config/swagger/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -1091,6 +1091,26 @@
}
}
},
"/network/enable-rounds": {
"get": {
"tags": [
"network"
],
"summary": "returns the activation rounds metric",
"responses": {
"200": {
"description": "successful operation",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/GenericResponse"
}
}
}
}
}
}
},
"/network/ratings": {
"get": {
"tags": [
Expand Down
5 changes: 5 additions & 0 deletions facade/baseFacade.go
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,11 @@ func (pf *ProxyFacade) GetEnableEpochsMetricsV2() (*data.GenericAPIResponse, err
return pf.nodeStatusProc.GetEnableEpochsMetricsV2()
}

// GetEnableRoundsMetrics retrieves the activation rounds
func (pf *ProxyFacade) GetEnableRoundsMetrics() (*data.GenericAPIResponse, error) {
return pf.nodeStatusProc.GetEnableRoundsMetrics()
}

// GetRatingsConfig retrieves the node's configuration's metrics
func (pf *ProxyFacade) GetRatingsConfig() (*data.GenericAPIResponse, error) {
return pf.nodeStatusProc.GetRatingsConfig()
Expand Down
2 changes: 2 additions & 0 deletions facade/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"github.com/multiversx/mx-chain-core-go/data/transaction"
"github.com/multiversx/mx-chain-core-go/data/vm"
crypto "github.com/multiversx/mx-chain-crypto-go"

"github.com/multiversx/mx-chain-proxy-go/common"
"github.com/multiversx/mx-chain-proxy-go/data"
)
Expand Down Expand Up @@ -93,6 +94,7 @@ type NodeStatusProcessor interface {
GetAllIssuedESDTs(tokenType string) (*data.GenericAPIResponse, error)
GetEnableEpochsMetrics() (*data.GenericAPIResponse, error)
GetEnableEpochsMetricsV2() (*data.GenericAPIResponse, error)
GetEnableRoundsMetrics() (*data.GenericAPIResponse, error)
GetDirectStakedInfo() (*data.GenericAPIResponse, error)
GetDelegatedInfo() (*data.GenericAPIResponse, error)
GetRatingsConfig() (*data.GenericAPIResponse, error)
Expand Down
10 changes: 10 additions & 0 deletions facade/mock/nodeStatusProcessorStub.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ type NodeStatusProcessorStub struct {
GetDelegatedInfoCalled func() (*data.GenericAPIResponse, error)
GetEnableEpochsMetricsCalled func() (*data.GenericAPIResponse, error)
GetEnableEpochsMetricsV2Called func() (*data.GenericAPIResponse, error)
GetEnableRoundsMetricsCalled func() (*data.GenericAPIResponse, error)
GetRatingsConfigCalled func() (*data.GenericAPIResponse, error)
GetGenesisNodesPubKeysCalled func() (*data.GenericAPIResponse, error)
GetGasConfigsCalled func() (*data.GenericAPIResponse, error)
Expand Down Expand Up @@ -101,6 +102,15 @@ func (stub *NodeStatusProcessorStub) GetEnableEpochsMetricsV2() (*data.GenericAP
return &data.GenericAPIResponse{}, nil
}

// GetEnableRoundsMetrics -
func (stub *NodeStatusProcessorStub) GetEnableRoundsMetrics() (*data.GenericAPIResponse, error) {
if stub.GetEnableRoundsMetricsCalled != nil {
return stub.GetEnableRoundsMetricsCalled()
}

return &data.GenericAPIResponse{}, nil
}

// GetRatingsConfig -
func (stub *NodeStatusProcessorStub) GetRatingsConfig() (*data.GenericAPIResponse, error) {
if stub.GetRatingsConfigCalled != nil {
Expand Down
30 changes: 30 additions & 0 deletions process/nodeStatusProcessor.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (

"github.com/multiversx/mx-chain-core-go/core"
"github.com/multiversx/mx-chain-core-go/core/check"

"github.com/multiversx/mx-chain-proxy-go/data"
)

Expand Down Expand Up @@ -50,6 +51,9 @@ const (
// EnableEpochsV2Path represents the path where an observer exposes all the activation epochs
EnableEpochsV2Path = "/network/enable-epochs-v2"

// EnableRoundsPath represents the path where an observer exposes all the activation rounds
EnableRoundsPath = "/network/enable-rounds"

// MetricCrossCheckBlockHeight is the metric that stores cross block height
MetricCrossCheckBlockHeight = "erd_cross_check_block_height"

Expand Down Expand Up @@ -149,6 +153,11 @@ func (nsp *NodeStatusProcessor) GetEnableEpochsMetricsV2() (*data.GenericAPIResp
return nsp.getEnableEpochsMetrics(EnableEpochsV2Path)
}

// GetEnableRoundsMetrics will simply forward the activation rounds config metrics from an observer
func (nsp *NodeStatusProcessor) GetEnableRoundsMetrics() (*data.GenericAPIResponse, error) {
return nsp.getEnableRoundsMetrics(EnableRoundsPath)
}

func (nsp *NodeStatusProcessor) getEnableEpochsMetrics(path string) (*data.GenericAPIResponse, error) {
observers, err := nsp.proc.GetAllObservers(data.AvailabilityRecent)
if err != nil {
Expand All @@ -171,6 +180,27 @@ func (nsp *NodeStatusProcessor) getEnableEpochsMetrics(path string) (*data.Gener
return nil, WrapObserversError(responseEnableEpochsMetrics.Error)
}

func (nsp *NodeStatusProcessor) getEnableRoundsMetrics(path string) (*data.GenericAPIResponse, error) {
observers, err := nsp.proc.GetAllObservers(data.AvailabilityRecent)
if err != nil {
return nil, err
}

responseEnableRoundsMetrics := data.GenericAPIResponse{}
for _, observer := range observers {
_, err := nsp.proc.CallGetRestEndPoint(observer.Address, path, &responseEnableRoundsMetrics)
if err != nil {
log.Error("enable rounds metrics request", "observer", observer.Address, "error", err.Error())
continue
}

log.Info("enable rounds metrics request", "shard ID", observer.ShardId, "observer", observer.Address)
return &responseEnableRoundsMetrics, nil
}

return nil, WrapObserversError(responseEnableRoundsMetrics.Error)
}

// GetAllIssuedESDTs will forward the issued ESDTs based on the provided type
func (nsp *NodeStatusProcessor) GetAllIssuedESDTs(tokenType string) (*data.GenericAPIResponse, error) {
if !data.IsValidEsdtPath(tokenType) && tokenType != "" {
Expand Down
78 changes: 78 additions & 0 deletions process/nodeStatusProcessor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -578,6 +578,84 @@ func TestNodeStatusProcessor_GetEnableEpochsMetricsGetObserversShouldErr(t *test
require.Nil(t, status)
}

func TestNodeStatusProcessor_GetEnableRoundsMetricsGetObserversShouldErr(t *testing.T) {
t.Parallel()

localErr := errors.New("local error")
nodeStatusProc, _ := NewNodeStatusProcessor(&mock.ProcessorStub{
GetAllObserversCalled: func(dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) {
return nil, localErr
},
},
&mock.GenericApiResponseCacherMock{},
time.Nanosecond,
)

status, err := nodeStatusProc.GetEnableRoundsMetrics()
require.Equal(t, localErr, err)
require.Nil(t, status)
}

func TestNodeStatusProcessor_GetEnableRoundsMetricsGetEndpointErr(t *testing.T) {
t.Parallel()

localErr := errors.New("local error")
nodesStatusProc, _ := NewNodeStatusProcessor(&mock.ProcessorStub{
GetAllObserversCalled: func(dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) {
return []*data.NodeData{
{Address: "addr1", ShardId: 0},
}, nil
},
CallGetRestEndPointCalled: func(address string, path string, value interface{}) (int, error) {
return 0, localErr
},
},
&mock.GenericApiResponseCacherMock{},
time.Nanosecond,
)

status, err := nodesStatusProc.GetEnableRoundsMetrics()
require.True(t, errors.Is(err, ErrSendingRequest))
require.Nil(t, status)
}

func TestNodeStatusProcessor_GetEnableRoundsMetricsShouldWork(t *testing.T) {
t.Parallel()

key := "SupernovaEnableRound"
expectedValue := float64(100)
nodesStatusProc, _ := NewNodeStatusProcessor(&mock.ProcessorStub{
GetAllObserversCalled: func(dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) {
return []*data.NodeData{
{Address: "addr1", ShardId: 0},
}, nil
},
CallGetRestEndPointCalled: func(address string, path string, value interface{}) (int, error) {
metricMap := map[string]interface{}{
key: expectedValue,
}
genericResp := &data.GenericAPIResponse{Data: metricMap}
genericRespBytes, _ := json.Marshal(genericResp)

return 0, json.Unmarshal(genericRespBytes, value)
},
},
&mock.GenericApiResponseCacherMock{},
time.Nanosecond,
)

genericResponse, err := nodesStatusProc.GetEnableRoundsMetrics()
require.Nil(t, err)
require.NotNil(t, genericResponse)

metricsMap, ok := genericResponse.Data.(map[string]interface{})
require.True(t, ok)

actualValue, ok := metricsMap[key]
require.True(t, ok)
require.Equal(t, expectedValue, actualValue)
}

func TestNodeStatusProcessor_GetRatingsConfigGetAllObserversShouldFail(t *testing.T) {
t.Parallel()

Expand Down
Loading