From c262e482178728035475df9dd89eeb749ef27106 Mon Sep 17 00:00:00 2001 From: Sergey <83376337+freak12techno@users.noreply.github.com> Date: Tue, 7 Jan 2025 20:33:11 +0300 Subject: [PATCH] chore: use fetcher controller (#87) * feat: use fetcher controller * chore: moved state to a separate module * chore: moved controller to a separate module * chore: add controller tests and fetcher stubs * chore: fixed typo * chore: use mutex in state * chore: add state test --- pkg/app.go | 39 ++----- pkg/constants/constants.go | 2 + pkg/controller/controller.go | 118 ++++++++++++++++++++ pkg/controller/controller_test.go | 24 ++++ pkg/fetchers/balance.go | 5 + pkg/fetchers/commission.go | 5 + pkg/fetchers/consumer_commission.go | 4 + pkg/fetchers/consumer_info.go | 4 + pkg/fetchers/consumer_validators.go | 5 + pkg/fetchers/delegations.go | 5 + pkg/fetchers/fetcher.go | 25 ++++- pkg/fetchers/has_to_validate.go | 5 + pkg/fetchers/inflation.go | 4 + pkg/fetchers/node_info.go | 5 + pkg/fetchers/price.go | 5 + pkg/fetchers/rewards.go | 5 + pkg/fetchers/self_delegation.go | 5 + pkg/fetchers/signing_info.go | 5 + pkg/fetchers/slashing_params.go | 5 + pkg/fetchers/staking_params.go | 5 + pkg/fetchers/stub.go | 41 +++++++ pkg/fetchers/supply.go | 7 +- pkg/fetchers/unbonds.go | 4 + pkg/fetchers/validators.go | 5 + pkg/generators/active_set_tokens.go | 7 +- pkg/generators/active_set_tokens_test.go | 3 +- pkg/generators/balance.go | 4 +- pkg/generators/commission.go | 4 +- pkg/generators/consumer_info.go | 4 +- pkg/generators/consumer_needs_to_sign.go | 7 +- pkg/generators/delegations.go | 4 +- pkg/generators/inflation.go | 4 +- pkg/generators/node_info.go | 4 +- pkg/generators/price.go | 4 +- pkg/generators/rewards.go | 4 +- pkg/generators/self_delegation.go | 4 +- pkg/generators/signing_info.go | 4 +- pkg/generators/single_validator_info.go | 4 +- pkg/generators/slashing_params.go | 4 +- pkg/generators/staking_params.go | 4 +- pkg/generators/supply.go | 4 +- pkg/generators/unbonds.go | 4 +- pkg/generators/validator_active.go | 11 +- pkg/generators/validator_commission_rate.go | 9 +- pkg/generators/validator_rank.go | 4 +- pkg/generators/validators_info.go | 7 +- pkg/state/state.go | 73 ++++++++++-- pkg/state/state_test.go | 56 ++++++++++ 48 files changed, 455 insertions(+), 119 deletions(-) create mode 100644 pkg/controller/controller.go create mode 100644 pkg/controller/controller_test.go create mode 100644 pkg/fetchers/stub.go create mode 100644 pkg/state/state_test.go diff --git a/pkg/app.go b/pkg/app.go index 642f521..d552c2b 100644 --- a/pkg/app.go +++ b/pkg/app.go @@ -2,15 +2,13 @@ package pkg import ( "context" + controllerPkg "main/pkg/controller" fetchersPkg "main/pkg/fetchers" "main/pkg/fs" generatorsPkg "main/pkg/generators" - statePkg "main/pkg/state" "main/pkg/tendermint" "main/pkg/tracing" - "main/pkg/types" "net/http" - "sync" "time" "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" @@ -44,6 +42,8 @@ type App struct { // Example: ActiveSetTokenGenerator generates a metric // based on ValidatorsFetcher and StakingParamsFetcher. Generators []generatorsPkg.Generator + + Controller *controllerPkg.Controller } func NewApp(configPath string, filesystem fs.FS, version string) *App { @@ -122,6 +122,8 @@ func NewApp(configPath string, filesystem fs.FS, version string) *App { generatorsPkg.NewSupplyGenerator(appConfig.Chains), } + controller := controllerPkg.NewController(fetchers, logger) + server := &http.Server{Addr: appConfig.ListenAddress, Handler: nil} return &App{ @@ -132,6 +134,7 @@ func NewApp(configPath string, filesystem fs.FS, version string) *App { Fetchers: fetchers, Generators: generators, Server: server, + Controller: controller, } } @@ -174,35 +177,7 @@ func (a *App) Handler(w http.ResponseWriter, r *http.Request) { registry := prometheus.NewRegistry() - var wg sync.WaitGroup - var mutex sync.Mutex - - var queryInfos []*types.QueryInfo - - state := statePkg.NewState() - - for _, fetchersExt := range a.Fetchers { - wg.Add(1) - - go func(fetcher fetchersPkg.Fetcher) { - childQuerierCtx, fetcherSpan := a.Tracer.Start( - rootSpanCtx, - "Fetcher "+string(fetcher.Name()), - trace.WithAttributes(attribute.String("fetcher", string(fetcher.Name()))), - ) - defer fetcherSpan.End() - - defer wg.Done() - data, fetcherQueryInfos := fetcher.Fetch(childQuerierCtx) - - mutex.Lock() - state.Set(fetcher.Name(), data) - queryInfos = append(queryInfos, fetcherQueryInfos...) - mutex.Unlock() - }(fetchersExt) - } - - wg.Wait() + state, queryInfos := a.Controller.Fetch(rootSpanCtx) queriesMetrics := NewQueriesMetrics(a.Config.Chains, queryInfos) registry.MustRegister(queriesMetrics.GetMetrics(rootSpanCtx)...) diff --git a/pkg/constants/constants.go b/pkg/constants/constants.go index 3e0b742..2e2ee67 100644 --- a/pkg/constants/constants.go +++ b/pkg/constants/constants.go @@ -24,6 +24,8 @@ const ( FetcherNameNodeInfo FetcherName = "node_info" FetcherNameInflation FetcherName = "inflation" FetcherNameSupply FetcherName = "supply" + FetcherNameStub1 FetcherName = "stub1" + FetcherNameStub2 FetcherName = "stub2" MetricsPrefix string = "cosmos_validators_exporter_" diff --git a/pkg/controller/controller.go b/pkg/controller/controller.go new file mode 100644 index 0000000..a4afb9b --- /dev/null +++ b/pkg/controller/controller.go @@ -0,0 +1,118 @@ +package controller + +import ( + "context" + "main/pkg/constants" + fetchersPkg "main/pkg/fetchers" + statePkg "main/pkg/state" + "main/pkg/types" + "sync" + + "github.com/rs/zerolog" +) + +type FetchersStatuses map[constants.FetcherName]bool + +func (s FetchersStatuses) IsAllDone(fetcherNames []constants.FetcherName) bool { + for _, fetcherName := range fetcherNames { + if _, ok := s[fetcherName]; !ok { + return false + } + } + + return true +} + +type Controller struct { + Fetchers fetchersPkg.Fetchers + Logger zerolog.Logger +} + +func NewController( + fetchers fetchersPkg.Fetchers, + logger *zerolog.Logger, +) *Controller { + return &Controller{ + Logger: logger.With(). + Str("component", "controller"). + Logger(), + Fetchers: fetchers, + } +} + +func (c *Controller) Fetch(ctx context.Context) ( + *statePkg.State, + []*types.QueryInfo, +) { + data := statePkg.NewState() + queries := []*types.QueryInfo{} + fetchersStatus := FetchersStatuses{} + + var mutex sync.Mutex + var wg sync.WaitGroup + + processFetcher := func(fetcher fetchersPkg.Fetcher) { + defer wg.Done() + + c.Logger.Trace().Str("name", string(fetcher.Name())).Msg("Processing fetcher...") + + mutex.Lock() + fetcherDependenciesData := data.GetData(fetcher.Dependencies()) + mutex.Unlock() + + fetcherData, fetcherQueries := fetcher.Fetch(ctx, fetcherDependenciesData...) + + mutex.Lock() + data.Set(fetcher.Name(), fetcherData) + queries = append(queries, fetcherQueries...) + fetchersStatus[fetcher.Name()] = true + mutex.Unlock() + + c.Logger.Trace(). + Str("name", string(fetcher.Name())). + Msg("Processed fetcher") + } + + for { + c.Logger.Trace().Msg("Processing all pending fetchers...") + + if fetchersStatus.IsAllDone(c.Fetchers.GetNames()) { + c.Logger.Trace().Msg("All fetchers are fetched.") + break + } + + fetchersToStart := fetchersPkg.Fetchers{} + + for _, fetcher := range c.Fetchers { + if _, ok := fetchersStatus[fetcher.Name()]; ok { + c.Logger.Trace(). + Str("name", string(fetcher.Name())). + Msg("Fetcher is already being processed or is processed, skipping.") + continue + } + + if !fetchersStatus.IsAllDone(fetcher.Dependencies()) { + c.Logger.Trace(). + Str("name", string(fetcher.Name())). + Msg("Fetcher's dependencies are not yet processed, skipping for now.") + continue + } + + fetchersToStart = append(fetchersToStart, fetcher) + } + + c.Logger.Trace(). + Strs("names", fetchersToStart.GetNamesAsString()). + Msg("Starting the following fetchers") + + wg.Add(len(fetchersToStart)) + + for _, fetcher := range fetchersToStart { + go processFetcher(fetcher) + } + + wg.Wait() + } + + return data, queries +} diff --git a/pkg/controller/controller_test.go b/pkg/controller/controller_test.go new file mode 100644 index 0000000..bba9ec8 --- /dev/null +++ b/pkg/controller/controller_test.go @@ -0,0 +1,24 @@ +package controller + +import ( + "context" + fetchersPkg "main/pkg/fetchers" + loggerPkg "main/pkg/logger" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestControllerFetcherEnabled(t *testing.T) { + t.Parallel() + + logger := loggerPkg.GetNopLogger() + controller := NewController(fetchersPkg.Fetchers{ + &fetchersPkg.StubFetcher1{}, + &fetchersPkg.StubFetcher2{}, + }, logger) + + data, queryInfos := controller.Fetch(context.Background()) + assert.Empty(t, queryInfos) + assert.Equal(t, 2, data.Length()) +} diff --git a/pkg/fetchers/balance.go b/pkg/fetchers/balance.go index 2e2296a..d0a0333 100644 --- a/pkg/fetchers/balance.go +++ b/pkg/fetchers/balance.go @@ -44,8 +44,13 @@ func NewBalanceFetcher( } } +func (q *BalanceFetcher) Dependencies() []constants.FetcherName { + return []constants.FetcherName{} +} + func (q *BalanceFetcher) Fetch( ctx context.Context, + data ...interface{}, ) (interface{}, []*types.QueryInfo) { q.queryInfos = []*types.QueryInfo{} q.allBalances = map[string]map[string][]types.Amount{} diff --git a/pkg/fetchers/commission.go b/pkg/fetchers/commission.go index efb0bfc..b16d406 100644 --- a/pkg/fetchers/commission.go +++ b/pkg/fetchers/commission.go @@ -37,8 +37,13 @@ func NewCommissionFetcher( } } +func (q *CommissionFetcher) Dependencies() []constants.FetcherName { + return []constants.FetcherName{} +} + func (q *CommissionFetcher) Fetch( ctx context.Context, + data ...interface{}, ) (interface{}, []*types.QueryInfo) { var queryInfos []*types.QueryInfo diff --git a/pkg/fetchers/consumer_commission.go b/pkg/fetchers/consumer_commission.go index de15916..056894e 100644 --- a/pkg/fetchers/consumer_commission.go +++ b/pkg/fetchers/consumer_commission.go @@ -43,8 +43,12 @@ func NewConsumerCommissionFetcher( } } +func (f *ConsumerCommissionFetcher) Dependencies() []constants.FetcherName { + return []constants.FetcherName{} +} func (f *ConsumerCommissionFetcher) Fetch( ctx context.Context, + data ...interface{}, ) (interface{}, []*types.QueryInfo) { f.queryInfos = []*types.QueryInfo{} f.data = map[string]map[string]*types.ConsumerCommissionResponse{} diff --git a/pkg/fetchers/consumer_info.go b/pkg/fetchers/consumer_info.go index 56c8bbc..ea47661 100644 --- a/pkg/fetchers/consumer_info.go +++ b/pkg/fetchers/consumer_info.go @@ -43,8 +43,12 @@ func NewConsumerInfoFetcher( } } +func (f *ConsumerInfoFetcher) Dependencies() []constants.FetcherName { + return []constants.FetcherName{} +} func (f *ConsumerInfoFetcher) Fetch( ctx context.Context, + data ...interface{}, ) (interface{}, []*types.QueryInfo) { f.queryInfos = []*types.QueryInfo{} f.allInfos = map[string]map[string]types.ConsumerChainInfo{} diff --git a/pkg/fetchers/consumer_validators.go b/pkg/fetchers/consumer_validators.go index 8342b14..dd05f60 100644 --- a/pkg/fetchers/consumer_validators.go +++ b/pkg/fetchers/consumer_validators.go @@ -43,8 +43,13 @@ func NewConsumerValidatorsFetcher( } } +func (f *ConsumerValidatorsFetcher) Dependencies() []constants.FetcherName { + return []constants.FetcherName{} +} + func (f *ConsumerValidatorsFetcher) Fetch( ctx context.Context, + data ...interface{}, ) (interface{}, []*types.QueryInfo) { f.queryInfos = []*types.QueryInfo{} f.allValidators = map[string]*types.ConsumerValidatorsResponse{} diff --git a/pkg/fetchers/delegations.go b/pkg/fetchers/delegations.go index 4c99c38..8239399 100644 --- a/pkg/fetchers/delegations.go +++ b/pkg/fetchers/delegations.go @@ -37,8 +37,13 @@ func NewDelegationsFetcher( } } +func (q *DelegationsFetcher) Dependencies() []constants.FetcherName { + return []constants.FetcherName{} +} + func (q *DelegationsFetcher) Fetch( ctx context.Context, + data ...interface{}, ) (interface{}, []*types.QueryInfo) { var queryInfos []*types.QueryInfo diff --git a/pkg/fetchers/fetcher.go b/pkg/fetchers/fetcher.go index fc2e003..9891950 100644 --- a/pkg/fetchers/fetcher.go +++ b/pkg/fetchers/fetcher.go @@ -7,6 +7,29 @@ import ( ) type Fetcher interface { - Fetch(ctx context.Context) (interface{}, []*types.QueryInfo) + Fetch(ctx context.Context, data ...interface{}) (interface{}, []*types.QueryInfo) + Dependencies() []constants.FetcherName Name() constants.FetcherName } + +type Fetchers []Fetcher + +func (f Fetchers) GetNames() []constants.FetcherName { + names := make([]constants.FetcherName, len(f)) + + for index, fetcher := range f { + names[index] = fetcher.Name() + } + + return names +} + +func (f Fetchers) GetNamesAsString() []string { + names := make([]string, len(f)) + + for index, fetcher := range f { + names[index] = string(fetcher.Name()) + } + + return names +} diff --git a/pkg/fetchers/has_to_validate.go b/pkg/fetchers/has_to_validate.go index 404c0e0..a73a5c6 100644 --- a/pkg/fetchers/has_to_validate.go +++ b/pkg/fetchers/has_to_validate.go @@ -43,8 +43,13 @@ func NewValidatorConsumersFetcher( } } +func (q *ValidatorConsumersFetcher) Dependencies() []constants.FetcherName { + return []constants.FetcherName{} +} + func (q *ValidatorConsumersFetcher) Fetch( ctx context.Context, + data ...interface{}, ) (interface{}, []*types.QueryInfo) { q.queryInfos = []*types.QueryInfo{} q.allValidatorsConsumers = map[string]map[string]map[string]bool{} diff --git a/pkg/fetchers/inflation.go b/pkg/fetchers/inflation.go index 5c025c8..dd33086 100644 --- a/pkg/fetchers/inflation.go +++ b/pkg/fetchers/inflation.go @@ -39,8 +39,12 @@ func NewInflationFetcher( } } +func (q *InflationFetcher) Dependencies() []constants.FetcherName { + return []constants.FetcherName{} +} func (q *InflationFetcher) Fetch( ctx context.Context, + data ...interface{}, ) (interface{}, []*types.QueryInfo) { var queryInfos []*types.QueryInfo diff --git a/pkg/fetchers/node_info.go b/pkg/fetchers/node_info.go index 21c949d..b040ebd 100644 --- a/pkg/fetchers/node_info.go +++ b/pkg/fetchers/node_info.go @@ -43,8 +43,13 @@ func NewNodeInfoFetcher( } } +func (q *NodeInfoFetcher) Dependencies() []constants.FetcherName { + return []constants.FetcherName{} +} + func (q *NodeInfoFetcher) Fetch( ctx context.Context, + data ...interface{}, ) (interface{}, []*types.QueryInfo) { q.queryInfos = []*types.QueryInfo{} q.allNodeInfos = map[string]*types.NodeInfoResponse{} diff --git a/pkg/fetchers/price.go b/pkg/fetchers/price.go index 9bd5937..1d25b45 100644 --- a/pkg/fetchers/price.go +++ b/pkg/fetchers/price.go @@ -48,8 +48,13 @@ func NewPriceFetcher( } } +func (q *PriceFetcher) Dependencies() []constants.FetcherName { + return []constants.FetcherName{} +} + func (q *PriceFetcher) Fetch( ctx context.Context, + data ...interface{}, ) (interface{}, []*types.QueryInfo) { queries := []*types.QueryInfo{} denomsByPriceFetcher := map[constants.PriceFetcherName][]price_fetchers.ChainWithDenom{} diff --git a/pkg/fetchers/rewards.go b/pkg/fetchers/rewards.go index b322e77..5566094 100644 --- a/pkg/fetchers/rewards.go +++ b/pkg/fetchers/rewards.go @@ -38,8 +38,13 @@ func NewRewardsFetcher( } } +func (q *RewardsFetcher) Dependencies() []constants.FetcherName { + return []constants.FetcherName{} +} + func (q *RewardsFetcher) Fetch( ctx context.Context, + data ...interface{}, ) (interface{}, []*types.QueryInfo) { var queryInfos []*types.QueryInfo diff --git a/pkg/fetchers/self_delegation.go b/pkg/fetchers/self_delegation.go index 7f068e8..0f51c7e 100644 --- a/pkg/fetchers/self_delegation.go +++ b/pkg/fetchers/self_delegation.go @@ -38,8 +38,13 @@ func NewSelfDelegationFetcher( } } +func (q *SelfDelegationFetcher) Dependencies() []constants.FetcherName { + return []constants.FetcherName{} +} + func (q *SelfDelegationFetcher) Fetch( ctx context.Context, + data ...interface{}, ) (interface{}, []*types.QueryInfo) { var queryInfos []*types.QueryInfo diff --git a/pkg/fetchers/signing_info.go b/pkg/fetchers/signing_info.go index ff3ea03..dbc80c4 100644 --- a/pkg/fetchers/signing_info.go +++ b/pkg/fetchers/signing_info.go @@ -44,8 +44,13 @@ func NewSigningInfoFetcher( } } +func (q *SigningInfoFetcher) Dependencies() []constants.FetcherName { + return []constants.FetcherName{} +} + func (q *SigningInfoFetcher) Fetch( ctx context.Context, + data ...interface{}, ) (interface{}, []*types.QueryInfo) { q.queryInfos = []*types.QueryInfo{} q.allSigningInfos = map[string]map[string]*types.SigningInfoResponse{} diff --git a/pkg/fetchers/slashing_params.go b/pkg/fetchers/slashing_params.go index 85e743e..b3af96b 100644 --- a/pkg/fetchers/slashing_params.go +++ b/pkg/fetchers/slashing_params.go @@ -43,8 +43,13 @@ func NewSlashingParamsFetcher( } } +func (q *SlashingParamsFetcher) Dependencies() []constants.FetcherName { + return []constants.FetcherName{} +} + func (q *SlashingParamsFetcher) Fetch( ctx context.Context, + data ...interface{}, ) (interface{}, []*types.QueryInfo) { q.queryInfos = []*types.QueryInfo{} q.allParams = map[string]*types.SlashingParamsResponse{} diff --git a/pkg/fetchers/staking_params.go b/pkg/fetchers/staking_params.go index f46d7ad..1444023 100644 --- a/pkg/fetchers/staking_params.go +++ b/pkg/fetchers/staking_params.go @@ -37,8 +37,13 @@ func NewStakingParamsFetcher( } } +func (q *StakingParamsFetcher) Dependencies() []constants.FetcherName { + return []constants.FetcherName{} +} + func (q *StakingParamsFetcher) Fetch( ctx context.Context, + data ...interface{}, ) (interface{}, []*types.QueryInfo) { var queryInfos []*types.QueryInfo diff --git a/pkg/fetchers/stub.go b/pkg/fetchers/stub.go new file mode 100644 index 0000000..604ed25 --- /dev/null +++ b/pkg/fetchers/stub.go @@ -0,0 +1,41 @@ +package fetchers + +import ( + "context" + "main/pkg/constants" + "main/pkg/types" +) + +type StubFetcher1 struct{} + +func (f *StubFetcher1) Name() constants.FetcherName { + return constants.FetcherNameStub1 +} + +func (f *StubFetcher1) Dependencies() []constants.FetcherName { + return []constants.FetcherName{} +} + +func (f *StubFetcher1) Fetch( + ctx context.Context, + data ...interface{}, +) (interface{}, []*types.QueryInfo) { + return nil, []*types.QueryInfo{} +} + +type StubFetcher2 struct{} + +func (f *StubFetcher2) Name() constants.FetcherName { + return constants.FetcherNameStub2 +} + +func (f *StubFetcher2) Dependencies() []constants.FetcherName { + return []constants.FetcherName{constants.FetcherNameStub1} +} + +func (f *StubFetcher2) Fetch( + ctx context.Context, + data ...interface{}, +) (interface{}, []*types.QueryInfo) { + return nil, []*types.QueryInfo{} +} diff --git a/pkg/fetchers/supply.go b/pkg/fetchers/supply.go index 8823720..5fc9b34 100644 --- a/pkg/fetchers/supply.go +++ b/pkg/fetchers/supply.go @@ -36,15 +36,20 @@ func NewSupplyFetcher( tracer trace.Tracer, ) *SupplyFetcher { return &SupplyFetcher{ - Logger: logger.With().Str("component", "balance_fetcher").Logger(), + Logger: logger.With().Str("component", "supply_fetcher").Logger(), Chains: chains, RPCs: rpcs, Tracer: tracer, } } +func (q *SupplyFetcher) Dependencies() []constants.FetcherName { + return []constants.FetcherName{} +} + func (q *SupplyFetcher) Fetch( ctx context.Context, + data ...interface{}, ) (interface{}, []*types.QueryInfo) { q.queryInfos = []*types.QueryInfo{} q.allSupplies = map[string][]types.Amount{} diff --git a/pkg/fetchers/unbonds.go b/pkg/fetchers/unbonds.go index cd9aeb7..f26ae3f 100644 --- a/pkg/fetchers/unbonds.go +++ b/pkg/fetchers/unbonds.go @@ -37,8 +37,12 @@ func NewUnbondsFetcher( } } +func (q *UnbondsFetcher) Dependencies() []constants.FetcherName { + return []constants.FetcherName{} +} func (q *UnbondsFetcher) Fetch( ctx context.Context, + data ...interface{}, ) (interface{}, []*types.QueryInfo) { var queryInfos []*types.QueryInfo diff --git a/pkg/fetchers/validators.go b/pkg/fetchers/validators.go index ce98dbb..f89ae81 100644 --- a/pkg/fetchers/validators.go +++ b/pkg/fetchers/validators.go @@ -37,8 +37,13 @@ func NewValidatorsFetcher( } } +func (q *ValidatorsFetcher) Dependencies() []constants.FetcherName { + return []constants.FetcherName{} +} + func (f *ValidatorsFetcher) Fetch( ctx context.Context, + data ...interface{}, ) (interface{}, []*types.QueryInfo) { var queryInfos []*types.QueryInfo diff --git a/pkg/generators/active_set_tokens.go b/pkg/generators/active_set_tokens.go index 2a17c2d..84e2045 100644 --- a/pkg/generators/active_set_tokens.go +++ b/pkg/generators/active_set_tokens.go @@ -21,19 +21,16 @@ func NewActiveSetTokensGenerator(chains []*configPkg.Chain) *ActiveSetTokensGene } func (g *ActiveSetTokensGenerator) Generate(state *statePkg.State) []prometheus.Collector { - validatorsRaw, ok := state.Get(constants.FetcherNameValidators) + validators, ok := statePkg.StateGet[fetchersPkg.ValidatorsData](state, constants.FetcherNameValidators) if !ok { return []prometheus.Collector{} } - stakingParamsRaw, ok := state.Get(constants.FetcherNameStakingParams) + stakingParams, ok := statePkg.StateGet[fetchersPkg.StakingParamsData](state, constants.FetcherNameStakingParams) if !ok { return []prometheus.Collector{} } - validators, _ := validatorsRaw.(fetchersPkg.ValidatorsData) - stakingParams, _ := stakingParamsRaw.(fetchersPkg.StakingParamsData) - activeSetTokensGauge := prometheus.NewGaugeVec( prometheus.GaugeOpts{ Name: constants.MetricsPrefix + "active_set_tokens", diff --git a/pkg/generators/active_set_tokens_test.go b/pkg/generators/active_set_tokens_test.go index 7b4c397..efe83d8 100644 --- a/pkg/generators/active_set_tokens_test.go +++ b/pkg/generators/active_set_tokens_test.go @@ -4,10 +4,11 @@ import ( "main/pkg/config" "main/pkg/constants" "main/pkg/fetchers" - statePkg "main/pkg/state" "main/pkg/types" "testing" + statePkg "main/pkg/state" + "github.com/guregu/null/v5" "cosmossdk.io/math" diff --git a/pkg/generators/balance.go b/pkg/generators/balance.go index eaf3e53..38c6ae6 100644 --- a/pkg/generators/balance.go +++ b/pkg/generators/balance.go @@ -18,7 +18,7 @@ func NewBalanceGenerator(chains []*config.Chain) *BalanceGenerator { } func (g *BalanceGenerator) Generate(state *statePkg.State) []prometheus.Collector { - dataRaw, ok := state.Get(constants.FetcherNameBalance) + data, ok := statePkg.StateGet[fetchersPkg.BalanceData](state, constants.FetcherNameBalance) if !ok { return []prometheus.Collector{} } @@ -31,8 +31,6 @@ func (g *BalanceGenerator) Generate(state *statePkg.State) []prometheus.Collecto []string{"chain", "address", "denom"}, ) - data, _ := dataRaw.(fetchersPkg.BalanceData) - for _, chain := range g.Chains { for _, consumer := range chain.ConsumerChains { consumerBalances, ok := data.Balances[consumer.Name] diff --git a/pkg/generators/commission.go b/pkg/generators/commission.go index 9bbdd53..580659c 100644 --- a/pkg/generators/commission.go +++ b/pkg/generators/commission.go @@ -18,7 +18,7 @@ func NewCommissionGenerator(chains []*config.Chain) *CommissionGenerator { } func (g *CommissionGenerator) Generate(state *statePkg.State) []prometheus.Collector { - dataRaw, ok := state.Get(constants.FetcherNameCommission) + data, ok := statePkg.StateGet[fetchersPkg.CommissionData](state, constants.FetcherNameCommission) if !ok { return []prometheus.Collector{} } @@ -31,8 +31,6 @@ func (g *CommissionGenerator) Generate(state *statePkg.State) []prometheus.Colle []string{"chain", "address", "denom"}, ) - data, _ := dataRaw.(fetchersPkg.CommissionData) - for _, chain := range g.Chains { chainCommissions, ok := data.Commissions[chain.Name] if !ok { diff --git a/pkg/generators/consumer_info.go b/pkg/generators/consumer_info.go index 789572f..e24e73a 100644 --- a/pkg/generators/consumer_info.go +++ b/pkg/generators/consumer_info.go @@ -20,13 +20,11 @@ func NewConsumerInfoGenerator(chains []*config.Chain) *ConsumerInfoGenerator { } func (g *ConsumerInfoGenerator) Generate(state *statePkg.State) []prometheus.Collector { - consumerInfosRaw, ok := state.Get(constants.FetcherNameConsumerInfo) + consumerInfos, ok := statePkg.StateGet[fetchersPkg.ConsumerInfoData](state, constants.FetcherNameConsumerInfo) if !ok { return []prometheus.Collector{} } - consumerInfos, _ := consumerInfosRaw.(fetchersPkg.ConsumerInfoData) - consumerInfoGauge := prometheus.NewGaugeVec( prometheus.GaugeOpts{ Name: constants.MetricsPrefix + "consumer_info", diff --git a/pkg/generators/consumer_needs_to_sign.go b/pkg/generators/consumer_needs_to_sign.go index a439ae4..4f5fb9c 100644 --- a/pkg/generators/consumer_needs_to_sign.go +++ b/pkg/generators/consumer_needs_to_sign.go @@ -19,19 +19,16 @@ func NewConsumerNeedsToSignGenerator(chains []*config.Chain) *ConsumerNeedsToSig } func (g *ConsumerNeedsToSignGenerator) Generate(state *statePkg.State) []prometheus.Collector { - allValidatorsConsumersRaw, ok := state.Get(constants.FetcherNameValidatorConsumers) + allValidatorsConsumers, ok := statePkg.StateGet[fetchersPkg.ValidatorConsumersData](state, constants.FetcherNameValidatorConsumers) if !ok { return []prometheus.Collector{} } - consumerInfosRaw, ok := state.Get(constants.FetcherNameConsumerInfo) + consumerInfos, ok := statePkg.StateGet[fetchersPkg.ConsumerInfoData](state, constants.FetcherNameConsumerInfo) if !ok { return []prometheus.Collector{} } - allValidatorsConsumers, _ := allValidatorsConsumersRaw.(fetchersPkg.ValidatorConsumersData) - consumerInfos, _ := consumerInfosRaw.(fetchersPkg.ConsumerInfoData) - needsToSignGauge := prometheus.NewGaugeVec( prometheus.GaugeOpts{ Name: constants.MetricsPrefix + "consumer_needs_to_sign", diff --git a/pkg/generators/delegations.go b/pkg/generators/delegations.go index f2fae2f..a2b1853 100644 --- a/pkg/generators/delegations.go +++ b/pkg/generators/delegations.go @@ -16,7 +16,7 @@ func NewDelegationsGenerator() *DelegationsGenerator { } func (g *DelegationsGenerator) Generate(state *statePkg.State) []prometheus.Collector { - dataRaw, ok := state.Get(constants.FetcherNameDelegations) + data, ok := statePkg.StateGet[fetchersPkg.DelegationsData](state, constants.FetcherNameDelegations) if !ok { return []prometheus.Collector{} } @@ -29,8 +29,6 @@ func (g *DelegationsGenerator) Generate(state *statePkg.State) []prometheus.Coll []string{"chain", "address"}, ) - data, _ := dataRaw.(fetchersPkg.DelegationsData) - for chain, allDelegations := range data.Delegations { for validator, delegations := range allDelegations { delegationsCountGauge.With(prometheus.Labels{ diff --git a/pkg/generators/inflation.go b/pkg/generators/inflation.go index 307c672..4b3cd41 100644 --- a/pkg/generators/inflation.go +++ b/pkg/generators/inflation.go @@ -16,7 +16,7 @@ func NewInflationGenerator() *InflationGenerator { } func (g *InflationGenerator) Generate(state *statePkg.State) []prometheus.Collector { - dataRaw, ok := state.Get(constants.FetcherNameInflation) + data, ok := statePkg.StateGet[fetchersPkg.InflationData](state, constants.FetcherNameInflation) if !ok { return []prometheus.Collector{} } @@ -29,8 +29,6 @@ func (g *InflationGenerator) Generate(state *statePkg.State) []prometheus.Collec []string{"chain"}, ) - data, _ := dataRaw.(fetchersPkg.InflationData) - for chain, inflation := range data.Inflation { inflationGauge.With(prometheus.Labels{ "chain": chain, diff --git a/pkg/generators/node_info.go b/pkg/generators/node_info.go index 46fe5d2..8a6770f 100644 --- a/pkg/generators/node_info.go +++ b/pkg/generators/node_info.go @@ -16,13 +16,11 @@ func NewNodeInfoGenerator() *NodeInfoGenerator { } func (g *NodeInfoGenerator) Generate(state *statePkg.State) []prometheus.Collector { - nodeInfosRaw, ok := state.Get(constants.FetcherNameNodeInfo) + nodeInfos, ok := statePkg.StateGet[fetchersPkg.NodeInfoData](state, constants.FetcherNameNodeInfo) if !ok { return []prometheus.Collector{} } - nodeInfos, _ := nodeInfosRaw.(fetchersPkg.NodeInfoData) - networkInfoGauge := prometheus.NewGaugeVec( prometheus.GaugeOpts{ Name: constants.MetricsPrefix + "chain_info", diff --git a/pkg/generators/price.go b/pkg/generators/price.go index 2c51b65..1494628 100644 --- a/pkg/generators/price.go +++ b/pkg/generators/price.go @@ -16,7 +16,7 @@ func NewPriceGenerator() *PriceGenerator { } func (g *PriceGenerator) Generate(state *statePkg.State) []prometheus.Collector { - dataRaw, ok := state.Get(constants.FetcherNamePrice) + data, ok := statePkg.StateGet[fetchersPkg.PriceData](state, constants.FetcherNamePrice) if !ok { return []prometheus.Collector{} } @@ -29,8 +29,6 @@ func (g *PriceGenerator) Generate(state *statePkg.State) []prometheus.Collector []string{"chain", "denom", "source", "base_currency"}, ) - data, _ := dataRaw.(fetchersPkg.PriceData) - for chainName, chainPrices := range data.Prices { for denom, price := range chainPrices { tokenPriceGauge.With(prometheus.Labels{ diff --git a/pkg/generators/rewards.go b/pkg/generators/rewards.go index b7902e2..cfa2b40 100644 --- a/pkg/generators/rewards.go +++ b/pkg/generators/rewards.go @@ -18,7 +18,7 @@ func NewRewardsGenerator(chains []*config.Chain) *RewardsGenerator { } func (g *RewardsGenerator) Generate(state *statePkg.State) []prometheus.Collector { - dataRaw, ok := state.Get(constants.FetcherNameRewards) + data, ok := statePkg.StateGet[fetchersPkg.RewardsData](state, constants.FetcherNameRewards) if !ok { return []prometheus.Collector{} } @@ -31,8 +31,6 @@ func (g *RewardsGenerator) Generate(state *statePkg.State) []prometheus.Collecto []string{"chain", "address", "denom"}, ) - data, _ := dataRaw.(fetchersPkg.RewardsData) - for _, chain := range g.Chains { chainRewards, ok := data.Rewards[chain.Name] if !ok { diff --git a/pkg/generators/self_delegation.go b/pkg/generators/self_delegation.go index 7cebf5e..945d6da 100644 --- a/pkg/generators/self_delegation.go +++ b/pkg/generators/self_delegation.go @@ -18,7 +18,7 @@ func NewSelfDelegationGenerator(chains []*config.Chain) *SelfDelegationGenerator } func (g *SelfDelegationGenerator) Generate(state *statePkg.State) []prometheus.Collector { - dataRaw, ok := state.Get(constants.FetcherNameSelfDelegation) + data, ok := statePkg.StateGet[fetchersPkg.SelfDelegationData](state, constants.FetcherNameSelfDelegation) if !ok { return []prometheus.Collector{} } @@ -31,8 +31,6 @@ func (g *SelfDelegationGenerator) Generate(state *statePkg.State) []prometheus.C []string{"chain", "address", "denom"}, ) - data, _ := dataRaw.(fetchersPkg.SelfDelegationData) - for _, chain := range g.Chains { chainDelegations, ok := data.Delegations[chain.Name] if !ok { diff --git a/pkg/generators/signing_info.go b/pkg/generators/signing_info.go index 7a1c24e..8255f1f 100644 --- a/pkg/generators/signing_info.go +++ b/pkg/generators/signing_info.go @@ -16,7 +16,7 @@ func NewSigningInfoGenerator() *SigningInfoGenerator { } func (g *SigningInfoGenerator) Generate(state *statePkg.State) []prometheus.Collector { - dataRaw, ok := state.Get(constants.FetcherNameSigningInfo) + data, ok := statePkg.StateGet[fetchersPkg.SigningInfoData](state, constants.FetcherNameSigningInfo) if !ok { return []prometheus.Collector{} } @@ -29,8 +29,6 @@ func (g *SigningInfoGenerator) Generate(state *statePkg.State) []prometheus.Coll []string{"chain", "address"}, ) - data, _ := dataRaw.(fetchersPkg.SigningInfoData) - for chain, commissions := range data.SigningInfos { for validator, signingInfo := range commissions { missedBlocksCounter := signingInfo.ValSigningInfo.MissedBlocksCounter.Int64() diff --git a/pkg/generators/single_validator_info.go b/pkg/generators/single_validator_info.go index 798faed..f887503 100644 --- a/pkg/generators/single_validator_info.go +++ b/pkg/generators/single_validator_info.go @@ -29,7 +29,7 @@ func NewSingleValidatorInfoGenerator( } func (g *SingleValidatorInfoGenerator) Generate(state *statePkg.State) []prometheus.Collector { - dataRaw, ok := state.Get(constants.FetcherNameValidators) + data, ok := statePkg.StateGet[fetchersPkg.ValidatorsData](state, constants.FetcherNameValidators) if !ok { return []prometheus.Collector{} } @@ -82,8 +82,6 @@ func (g *SingleValidatorInfoGenerator) Generate(state *statePkg.State) []prometh []string{"chain", "address", "denom"}, ) - data, _ := dataRaw.(fetchersPkg.ValidatorsData) - for _, chain := range g.Chains { chainValidators, ok := data.Validators[chain.Name] if !ok { diff --git a/pkg/generators/slashing_params.go b/pkg/generators/slashing_params.go index bff627b..cb9382d 100644 --- a/pkg/generators/slashing_params.go +++ b/pkg/generators/slashing_params.go @@ -16,7 +16,7 @@ func NewSlashingParamsGenerator() *SlashingParamsGenerator { } func (g *SlashingParamsGenerator) Generate(state *statePkg.State) []prometheus.Collector { - dataRaw, ok := state.Get(constants.FetcherNameSlashingParams) + data, ok := statePkg.StateGet[fetchersPkg.SlashingParamsData](state, constants.FetcherNameSlashingParams) if !ok { return []prometheus.Collector{} } @@ -29,8 +29,6 @@ func (g *SlashingParamsGenerator) Generate(state *statePkg.State) []prometheus.C []string{"chain"}, ) - data, _ := dataRaw.(fetchersPkg.SlashingParamsData) - for chain, params := range data.Params { blocksWindowGauge.With(prometheus.Labels{ "chain": chain, diff --git a/pkg/generators/staking_params.go b/pkg/generators/staking_params.go index 0f7c47f..1e18453 100644 --- a/pkg/generators/staking_params.go +++ b/pkg/generators/staking_params.go @@ -16,7 +16,7 @@ func NewStakingParamsGenerator() *StakingParamsGenerator { } func (g *StakingParamsGenerator) Generate(state *statePkg.State) []prometheus.Collector { - dataRaw, ok := state.Get(constants.FetcherNameStakingParams) + data, ok := statePkg.StateGet[fetchersPkg.StakingParamsData](state, constants.FetcherNameStakingParams) if !ok { return []prometheus.Collector{} } @@ -29,8 +29,6 @@ func (g *StakingParamsGenerator) Generate(state *statePkg.State) []prometheus.Co []string{"chain"}, ) - data, _ := dataRaw.(fetchersPkg.StakingParamsData) - for chain, params := range data.Params { maxValidators := int64(params.StakingParams.MaxValidators) if maxValidators >= 0 { diff --git a/pkg/generators/supply.go b/pkg/generators/supply.go index f57cf60..6f0e472 100644 --- a/pkg/generators/supply.go +++ b/pkg/generators/supply.go @@ -18,7 +18,7 @@ func NewSupplyGenerator(chains []*config.Chain) *SupplyGenerator { } func (g *SupplyGenerator) Generate(state *statePkg.State) []prometheus.Collector { - dataRaw, ok := state.Get(constants.FetcherNameSupply) + data, ok := statePkg.StateGet[fetchersPkg.SupplyData](state, constants.FetcherNameSupply) if !ok { return []prometheus.Collector{} } @@ -31,8 +31,6 @@ func (g *SupplyGenerator) Generate(state *statePkg.State) []prometheus.Collector []string{"chain", "denom"}, ) - data, _ := dataRaw.(fetchersPkg.SupplyData) - for _, chain := range g.Chains { for _, consumer := range chain.ConsumerChains { consumerSupplies, ok := data.Supplies[consumer.Name] diff --git a/pkg/generators/unbonds.go b/pkg/generators/unbonds.go index a795b49..c81b47f 100644 --- a/pkg/generators/unbonds.go +++ b/pkg/generators/unbonds.go @@ -16,7 +16,7 @@ func NewUnbondsGenerator() *UnbondsGenerator { } func (g *UnbondsGenerator) Generate(state *statePkg.State) []prometheus.Collector { - dataRaw, ok := state.Get(constants.FetcherNameUnbonds) + data, ok := statePkg.StateGet[fetchersPkg.UnbondsData](state, constants.FetcherNameUnbonds) if !ok { return []prometheus.Collector{} } @@ -29,8 +29,6 @@ func (g *UnbondsGenerator) Generate(state *statePkg.State) []prometheus.Collecto []string{"chain", "address"}, ) - data, _ := dataRaw.(fetchersPkg.UnbondsData) - for chain, commissions := range data.Unbonds { for validator, unbonds := range commissions { unbondsCountGauge.With(prometheus.Labels{ diff --git a/pkg/generators/validator_active.go b/pkg/generators/validator_active.go index 83c59ee..296408d 100644 --- a/pkg/generators/validator_active.go +++ b/pkg/generators/validator_active.go @@ -29,12 +29,12 @@ func NewValidatorActiveGenerator( } func (g *ValidatorActiveGenerator) Generate(state *statePkg.State) []prometheus.Collector { - dataRaw, ok := state.Get(constants.FetcherNameValidators) + validators, ok := statePkg.StateGet[fetchersPkg.ValidatorsData](state, constants.FetcherNameValidators) if !ok { return []prometheus.Collector{} } - consumersDataRaw, ok := state.Get(constants.FetcherNameConsumerValidators) + allConsumerValidators, ok := statePkg.StateGet[fetchersPkg.ConsumerValidatorsData](state, constants.FetcherNameConsumerValidators) if !ok { return []prometheus.Collector{} } @@ -47,11 +47,8 @@ func (g *ValidatorActiveGenerator) Generate(state *statePkg.State) []prometheus. []string{"chain", "address"}, ) - data, _ := dataRaw.(fetchersPkg.ValidatorsData) - consumersData, _ := consumersDataRaw.(fetchersPkg.ConsumerValidatorsData) - for _, chain := range g.Chains { - chainValidators, ok := data.Validators[chain.Name] + chainValidators, ok := validators.Validators[chain.Name] if !ok { g.Logger.Warn(). Str("chain", chain.Name). @@ -94,7 +91,7 @@ func (g *ValidatorActiveGenerator) Generate(state *statePkg.State) []prometheus. } for _, consumer := range chain.ConsumerChains { - consumerValidators, ok := consumersData.Validators[consumer.Name] + consumerValidators, ok := allConsumerValidators.Validators[consumer.Name] if !ok { continue } diff --git a/pkg/generators/validator_commission_rate.go b/pkg/generators/validator_commission_rate.go index f58307a..9966399 100644 --- a/pkg/generators/validator_commission_rate.go +++ b/pkg/generators/validator_commission_rate.go @@ -29,12 +29,12 @@ func NewValidatorCommissionRateGenerator( } func (g *ValidatorCommissionRateGenerator) Generate(state *statePkg.State) []prometheus.Collector { - consumerCommissionsRaw, ok := state.Get(constants.FetcherNameConsumerCommission) + consumerCommissions, ok := statePkg.StateGet[fetchersPkg.ConsumerCommissionData](state, constants.FetcherNameConsumerCommission) if !ok { return []prometheus.Collector{} } - validatorsRaw, ok := state.Get(constants.FetcherNameValidators) + validators, ok := statePkg.StateGet[fetchersPkg.ValidatorsData](state, constants.FetcherNameValidators) if !ok { return []prometheus.Collector{} } @@ -47,9 +47,6 @@ func (g *ValidatorCommissionRateGenerator) Generate(state *statePkg.State) []pro []string{"chain", "address"}, ) - consumerCommission, _ := consumerCommissionsRaw.(fetchersPkg.ConsumerCommissionData) - validators, _ := validatorsRaw.(fetchersPkg.ValidatorsData) - for _, chain := range g.Chains { chainValidators, ok := validators.Validators[chain.Name] if !ok { @@ -87,7 +84,7 @@ func (g *ValidatorCommissionRateGenerator) Generate(state *statePkg.State) []pro } for _, consumer := range chain.ConsumerChains { - consumerValidators, ok := consumerCommission.Commissions[consumer.Name] + consumerValidators, ok := consumerCommissions.Commissions[consumer.Name] if !ok { continue } diff --git a/pkg/generators/validator_rank.go b/pkg/generators/validator_rank.go index ef9d1fb..8e1a10d 100644 --- a/pkg/generators/validator_rank.go +++ b/pkg/generators/validator_rank.go @@ -30,7 +30,7 @@ func NewValidatorRankGenerator( } func (g *ValidatorRankGenerator) Generate(state *statePkg.State) []prometheus.Collector { - dataRaw, ok := state.Get(constants.FetcherNameValidators) + data, ok := statePkg.StateGet[fetchersPkg.ValidatorsData](state, constants.FetcherNameValidators) if !ok { return []prometheus.Collector{} } @@ -43,8 +43,6 @@ func (g *ValidatorRankGenerator) Generate(state *statePkg.State) []prometheus.Co []string{"chain", "address"}, ) - data, _ := dataRaw.(fetchersPkg.ValidatorsData) - for _, chain := range g.Chains { chainValidators, ok := data.Validators[chain.Name] if !ok { diff --git a/pkg/generators/validators_info.go b/pkg/generators/validators_info.go index 9fc0fd9..a5ff3c8 100644 --- a/pkg/generators/validators_info.go +++ b/pkg/generators/validators_info.go @@ -22,12 +22,12 @@ func NewValidatorsInfoGenerator(chains []*config.Chain) *ValidatorsInfoGenerator } func (g *ValidatorsInfoGenerator) Generate(state *statePkg.State) []prometheus.Collector { - dataRaw, ok := state.Get(constants.FetcherNameValidators) + data, ok := statePkg.StateGet[fetchersPkg.ValidatorsData](state, constants.FetcherNameValidators) if !ok { return []prometheus.Collector{} } - consumersDataRaw, ok := state.Get(constants.FetcherNameConsumerValidators) + consumersData, ok := statePkg.StateGet[fetchersPkg.ConsumerValidatorsData](state, constants.FetcherNameConsumerValidators) if !ok { return []prometheus.Collector{} } @@ -48,9 +48,6 @@ func (g *ValidatorsInfoGenerator) Generate(state *statePkg.State) []prometheus.C []string{"chain", "denom"}, ) - data, _ := dataRaw.(fetchersPkg.ValidatorsData) - consumersData, _ := consumersDataRaw.(fetchersPkg.ConsumerValidatorsData) - for _, chain := range g.Chains { validators, ok := data.Validators[chain.Name] if !ok { diff --git a/pkg/state/state.go b/pkg/state/state.go index 1283821..68e6abf 100644 --- a/pkg/state/state.go +++ b/pkg/state/state.go @@ -1,31 +1,88 @@ package state import ( + "fmt" "main/pkg/constants" + "reflect" "sync" ) type State struct { - state map[constants.FetcherName]interface{} mutex sync.Mutex + data map[constants.FetcherName]interface{} } func NewState() *State { return &State{ - state: map[constants.FetcherName]interface{}{}, + data: map[constants.FetcherName]interface{}{}, } } -func (s *State) Set(key constants.FetcherName, value interface{}) { +func (s *State) GetData(fetcherNames []constants.FetcherName) []interface{} { s.mutex.Lock() - s.state[key] = value - s.mutex.Unlock() + defer s.mutex.Unlock() + + data := make([]interface{}, len(fetcherNames)) + + for index, fetcherName := range fetcherNames { + data[index] = s.data[fetcherName] + } + + return data +} + +func (s *State) Set(fetcherName constants.FetcherName, data interface{}) { + s.mutex.Lock() + defer s.mutex.Unlock() + + s.data[fetcherName] = data +} + +func (s *State) Get(fetcherName constants.FetcherName) (interface{}, bool) { + s.mutex.Lock() + defer s.mutex.Unlock() + + data, found := s.data[fetcherName] + return data, found } -func (s *State) Get(key constants.FetcherName) (interface{}, bool) { +func (s *State) Length() int { s.mutex.Lock() defer s.mutex.Unlock() - value, found := s.state[key] - return value, found + return len(s.data) +} + +func StateGet[T any](state *State, fetcherName constants.FetcherName) (T, bool) { + var zero T + + dataRaw, found := state.Get(fetcherName) + if !found { + return zero, false + } + + return Convert[T](dataRaw) +} + +func Convert[T any](input interface{}) (T, bool) { + var zero T + + if input == nil { + return zero, false + } + + data, converted := input.(T) + if !converted { + panic(fmt.Sprintf( + "Error converting data: expected %s, got %s", + reflect.TypeOf(zero).String(), + reflect.TypeOf(input).String(), + )) + } + + if reflect.ValueOf(data).Kind() == reflect.Ptr && reflect.ValueOf(data).IsNil() { + return zero, false + } + + return data, true } diff --git a/pkg/state/state_test.go b/pkg/state/state_test.go new file mode 100644 index 0000000..c63ae59 --- /dev/null +++ b/pkg/state/state_test.go @@ -0,0 +1,56 @@ +package state + +import ( + "main/pkg/constants" + fetchersPkg "main/pkg/fetchers" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestStateGetNoValue(t *testing.T) { + t.Parallel() + + state := NewState() + value, found := state.Get(constants.FetcherNameCommission) + require.False(t, found) + require.Nil(t, value) +} + +func TestStateConvertNoValue(t *testing.T) { + t.Parallel() + + value, found := Convert[fetchersPkg.CommissionData](nil) + require.False(t, found) + require.Zero(t, value) +} + +func TestStateConvertWrongType(t *testing.T) { + t.Parallel() + + defer func() { + if r := recover(); r == nil { + require.Fail(t, "Expected to have a panic here!") + } + }() + + Convert[int64]("string") +} + +func TestStateConvertNilPointer(t *testing.T) { + t.Parallel() + + var data *fetchersPkg.CommissionData + + value, found := Convert[*fetchersPkg.CommissionData](data) + require.False(t, found) + require.Zero(t, value) +} + +func TestStateConvertOk(t *testing.T) { + t.Parallel() + + value, found := Convert[string]("string") + require.True(t, found) + require.Equal(t, "string", value) +}