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

chore: refactor errors #55

Merged
merged 6 commits into from
Dec 7, 2023
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 .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ main
cover.out
cosmos-proposals-checker
dist
**/.DS_Store
2 changes: 1 addition & 1 deletion pkg/events/proposals_query_error.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (

type ProposalsQueryErrorEvent struct {
Chain *types.Chain
Error error
Error *types.QueryError
}

func (e ProposalsQueryErrorEvent) Name() string {
Expand Down
2 changes: 1 addition & 1 deletion pkg/events/vote_query_error.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
type VoteQueryError struct {
Chain *types.Chain
Proposal types.Proposal
Error error
Error *types.QueryError
}

func (e VoteQueryError) Name() string {
Expand Down
11 changes: 8 additions & 3 deletions pkg/report/generator_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package report

import (
"errors"
"main/pkg/events"
"testing"

Expand All @@ -20,7 +21,9 @@ func TestReportGeneratorWithProposalError(t *testing.T) {
newState := state.State{
ChainInfos: map[string]*state.ChainInfo{
"chain": {
ProposalsError: types.NewJSONError("test error"),
ProposalsError: &types.QueryError{
QueryError: errors.New("test error"),
},
},
},
}
Expand All @@ -34,7 +37,7 @@ func TestReportGeneratorWithProposalError(t *testing.T) {

entry, ok := report.Entries[0].(events.ProposalsQueryErrorEvent)
assert.True(t, ok, "Expected to have a proposal query error!")
assert.Equal(t, "test error", entry.Error.Error(), "Error text mismatch!")
assert.Equal(t, "test error", entry.Error.QueryError.Error(), "Error text mismatch!")
}

func TestReportGeneratorWithVoteError(t *testing.T) {
Expand All @@ -53,7 +56,9 @@ func TestReportGeneratorWithVoteError(t *testing.T) {
},
Votes: map[string]state.ProposalVote{
"wallet": {
Error: types.NewJSONError("test error"),
Error: &types.QueryError{
QueryError: errors.New("test error"),
},
},
},
},
Expand Down
2 changes: 1 addition & 1 deletion pkg/state/generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@
}

if err != nil {
proposalVote.Error = types.NewJSONError(err.Error())
proposalVote.Error = err

Check warning on line 127 in pkg/state/generator.go

View check run for this annotation

Codecov / codecov/patch

pkg/state/generator.go#L127

Added line #L127 was not covered by tests
} else {
proposalVote.Vote = voteResponse.Vote
}
Expand Down
8 changes: 4 additions & 4 deletions pkg/state/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
type ProposalVote struct {
Wallet *types.Wallet
Vote *types.Vote
Error *types.JSONError
Error *types.QueryError
}

func (v ProposalVote) HasVoted() bool {
Expand All @@ -26,7 +26,7 @@ type WalletVotes struct {
type ChainInfo struct {
Chain *types.Chain
ProposalVotes map[string]WalletVotes
ProposalsError *types.JSONError
ProposalsError *types.QueryError
}

func (c ChainInfo) HasProposalsError() bool {
Expand Down Expand Up @@ -61,10 +61,10 @@ func (s *State) SetVote(chain *types.Chain, proposal types.Proposal, wallet *typ
s.ChainInfos[chain.Name].ProposalVotes[proposal.ID].Votes[wallet.Address] = vote
}

func (s *State) SetChainProposalsError(chain *types.Chain, err error) {
func (s *State) SetChainProposalsError(chain *types.Chain, err *types.QueryError) {
s.ChainInfos[chain.Name] = &ChainInfo{
Chain: chain,
ProposalsError: types.NewJSONError(err.Error()),
ProposalsError: err,
}
}

Expand Down
12 changes: 8 additions & 4 deletions pkg/state/state_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,15 @@ func TestSetProposalErrorWithoutChainInfo(t *testing.T) {
t.Parallel()

state := NewState()
state.SetChainProposalsError(&types.Chain{Name: "test"}, errors.New("test error"))
state.SetChainProposalsError(&types.Chain{Name: "test"}, &types.QueryError{
QueryError: errors.New("test error"),
})

hasError2 := state.ChainInfos["test"].HasProposalsError()
assert.True(t, hasError2, "Chain info should have a proposal error!")

err := state.ChainInfos["test"].ProposalsError
assert.Equal(t, "test error", err.Error(), "Errors text should match!")
assert.Equal(t, "test error", err.QueryError.Error(), "Errors text should match!")
}

func TestSetVotes(t *testing.T) {
Expand Down Expand Up @@ -83,13 +85,15 @@ func TestSetProposalErrorWithChainInfo(t *testing.T) {
hasError := state.ChainInfos["test"].HasProposalsError()
assert.False(t, hasError, "Chain info should not have a proposal error!")

state.SetChainProposalsError(&types.Chain{Name: "test"}, errors.New("test error"))
state.SetChainProposalsError(&types.Chain{Name: "test"}, &types.QueryError{
QueryError: errors.New("test error"),
})

hasError2 := state.ChainInfos["test"].HasProposalsError()
assert.True(t, hasError2, "Chain info should have a proposal error!")

err := state.ChainInfos["test"].ProposalsError
assert.Equal(t, "test error", err.Error(), "Errors text should match!")
assert.Equal(t, "test error", err.QueryError.Error(), "Errors text should match!")
}

func TestGetVoteWithoutChainInfo(t *testing.T) {
Expand Down
87 changes: 53 additions & 34 deletions pkg/tendermint/tendermint.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,15 @@ func NewRPC(chainConfig *types.Chain, logger zerolog.Logger) *RPC {
}
}

func (rpc *RPC) GetAllProposals() ([]types.Proposal, error) {
func (rpc *RPC) GetAllProposals() ([]types.Proposal, *types.QueryError) {
if rpc.ProposalsType == "v1" {
return rpc.GetAllV1Proposals()
}

return rpc.GetAllV1beta1Proposals()
}

func (rpc *RPC) GetAllV1beta1Proposals() ([]types.Proposal, error) {
func (rpc *RPC) GetAllV1beta1Proposals() ([]types.Proposal, *types.QueryError) {
proposals := []types.Proposal{}
offset := 0

Expand All @@ -51,12 +51,17 @@ func (rpc *RPC) GetAllV1beta1Proposals() ([]types.Proposal, error) {
)

var batchProposals types.V1Beta1ProposalsRPCResponse
if err := rpc.Get(url, &batchProposals); err != nil {
return nil, err
if errs := rpc.Get(url, &batchProposals); len(errs) > 0 {
return nil, &types.QueryError{
QueryError: nil,
NodeErrors: errs,
}
}

if batchProposals.Message != "" {
return nil, errors.New(batchProposals.Message)
return nil, &types.QueryError{
QueryError: errors.New(batchProposals.Message),
}
}

parsedProposals := utils.Map(batchProposals.Proposals, func(p types.V1beta1Proposal) types.Proposal {
Expand All @@ -73,7 +78,7 @@ func (rpc *RPC) GetAllV1beta1Proposals() ([]types.Proposal, error) {
return proposals, nil
}

func (rpc *RPC) GetAllV1Proposals() ([]types.Proposal, error) {
func (rpc *RPC) GetAllV1Proposals() ([]types.Proposal, *types.QueryError) {
proposals := []types.Proposal{}
offset := 0

Expand All @@ -86,12 +91,17 @@ func (rpc *RPC) GetAllV1Proposals() ([]types.Proposal, error) {
)

var batchProposals types.V1ProposalsRPCResponse
if err := rpc.Get(url, &batchProposals); err != nil {
return nil, err
if errs := rpc.Get(url, &batchProposals); len(errs) > 0 {
return nil, &types.QueryError{
QueryError: nil,
NodeErrors: errs,
}
}

if batchProposals.Message != "" {
return nil, errors.New(batchProposals.Message)
return nil, &types.QueryError{
QueryError: errors.New(batchProposals.Message),
}
}

parsedProposals := utils.Map(batchProposals.Proposals, func(p types.V1Proposal) types.Proposal {
Expand All @@ -108,63 +118,77 @@ func (rpc *RPC) GetAllV1Proposals() ([]types.Proposal, error) {
return proposals, nil
}

func (rpc *RPC) GetVote(proposal, voter string) (*types.VoteRPCResponse, error) {
func (rpc *RPC) GetVote(proposal, voter string) (*types.VoteRPCResponse, *types.QueryError) {
url := fmt.Sprintf(
"/cosmos/gov/v1beta1/proposals/%s/votes/%s",
proposal,
voter,
)

var vote types.VoteRPCResponse
if err := rpc.Get(url, &vote); err != nil {
return nil, err
if errs := rpc.Get(url, &vote); len(errs) > 0 {
return nil, &types.QueryError{
QueryError: nil,
NodeErrors: errs,
}
}

if vote.IsError() && !strings.Contains(vote.Message, "not found") {
return nil, errors.New(vote.Message)
return nil, &types.QueryError{
QueryError: errors.New(vote.Message),
}
}

return &vote, nil
}

func (rpc *RPC) GetTally(proposal string) (*types.TallyRPCResponse, error) {
func (rpc *RPC) GetTally(proposal string) (*types.TallyRPCResponse, *types.QueryError) {
url := fmt.Sprintf(
"/cosmos/gov/v1beta1/proposals/%s/tally",
proposal,
)

var tally types.TallyRPCResponse
if err := rpc.Get(url, &tally); err != nil {
return nil, err
if errs := rpc.Get(url, &tally); len(errs) > 0 {
return nil, &types.QueryError{
QueryError: nil,
NodeErrors: errs,
}
}

return &tally, nil
}

func (rpc *RPC) GetStakingPool() (*types.PoolRPCResponse, error) {
func (rpc *RPC) GetStakingPool() (*types.PoolRPCResponse, *types.QueryError) {
url := "/cosmos/staking/v1beta1/pool"

var pool types.PoolRPCResponse
if err := rpc.Get(url, &pool); err != nil {
return nil, err
if errs := rpc.Get(url, &pool); len(errs) > 0 {
return nil, &types.QueryError{
QueryError: nil,
NodeErrors: errs,
}
}

return &pool, nil
}

func (rpc *RPC) GetGovParams(paramsType string) (*types.ParamsResponse, error) {
func (rpc *RPC) GetGovParams(paramsType string) (*types.ParamsResponse, *types.QueryError) {
url := fmt.Sprintf("/cosmos/gov/v1beta1/params/%s", paramsType)

var pool types.ParamsResponse
if err := rpc.Get(url, &pool); err != nil {
return nil, err
if errs := rpc.Get(url, &pool); len(errs) > 0 {
return nil, &types.QueryError{
QueryError: nil,
NodeErrors: errs,
}
}

return &pool, nil
}

func (rpc *RPC) Get(url string, target interface{}) error {
nodeErrors := make([]error, len(rpc.URLs))
func (rpc *RPC) Get(url string, target interface{}) []types.NodeError {
nodeErrors := make([]types.NodeError, len(rpc.URLs))

for index, lcd := range rpc.URLs {
fullURL := lcd + url
Expand All @@ -180,19 +204,14 @@ func (rpc *RPC) Get(url string, target interface{}) error {
}

rpc.Logger.Warn().Str("url", fullURL).Err(err).Msg("LCD request failed")
nodeErrors[index] = err
nodeErrors[index] = types.NodeError{
Node: lcd,
Error: types.NewJSONError(err),
}
}

rpc.Logger.Warn().Str("url", url).Msg("All LCD requests failed")

var sb strings.Builder

sb.WriteString("All LCD requests failed:\n")
for index, url := range rpc.URLs {
sb.WriteString(fmt.Sprintf("#%d: %s -> %s\n", index+1, url, nodeErrors[index]))
}

return fmt.Errorf(sb.String())
return nodeErrors
}

func (rpc *RPC) GetFull(url string, target interface{}) error {
Expand Down
2 changes: 1 addition & 1 deletion pkg/types/chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ type Chain struct {
Explorer *Explorer `toml:"explorer"`
}

func (c *Chain) Validate() error {
func (c Chain) Validate() error {
if c.Name == "" {
return fmt.Errorf("empty chain name")
}
Expand Down
35 changes: 32 additions & 3 deletions pkg/types/error.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
package types

import "encoding/json"
import (
"encoding/json"
"fmt"
"strings"
)

type JSONError struct {
error string
}

func NewJSONError(err string) *JSONError {
return &JSONError{error: err}
func NewJSONError(err error) JSONError {
return JSONError{error: err.Error()}
}

func (e *JSONError) Error() string {
Expand All @@ -22,3 +26,28 @@ func (e *JSONError) UnmarshalJSON(data []byte) error {
e.error = string(data)
return nil
}

type NodeError struct {
Node string
Error JSONError
}

type QueryError struct {
QueryError error
NodeErrors []NodeError
}

func (q QueryError) Error() string {
if q.QueryError != nil {
return q.QueryError.Error()
}

var sb strings.Builder

sb.WriteString("All LCD requests failed:\n")
for index, nodeError := range q.NodeErrors {
sb.WriteString(fmt.Sprintf("#%d: %s -> %s\n", index+1, nodeError.Node, nodeError.Error.error))
}

return sb.String()
}
Loading
Loading