Skip to content

Commit

Permalink
web,cmd: Generalize chains and nodes API
Browse files Browse the repository at this point in the history
Make it easier to add new LOOPs by reducing the amount of boilerplate
  • Loading branch information
archseer committed Dec 12, 2024
1 parent 60f5569 commit e26dabb
Show file tree
Hide file tree
Showing 45 changed files with 856 additions and 1,333 deletions.
12 changes: 3 additions & 9 deletions core/cmd/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -290,15 +290,9 @@ func NewApp(s *Shell) *cli.App {
},
},
{
Name: "chains",
Usage: "Commands for handling chain configuration",
Subcommands: cli.Commands{
chainCommand("EVM", EVMChainClient(s), cli.Int64Flag{Name: "id", Usage: "chain ID"}),
chainCommand("Cosmos", CosmosChainClient(s), cli.StringFlag{Name: "id", Usage: "chain ID"}),
chainCommand("Solana", SolanaChainClient(s),
cli.StringFlag{Name: "id", Usage: "chain ID, options: [mainnet, testnet, devnet, localnet]"}),
chainCommand("StarkNet", StarkNetChainClient(s), cli.StringFlag{Name: "id", Usage: "chain ID"}),
},
Name: "chains",
Usage: "Commands for handling chain configuration",
Subcommands: initChainSubCmds(s),
},
{
Name: "nodes",
Expand Down
63 changes: 59 additions & 4 deletions core/cmd/chains_commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,15 @@ package cmd

import (
"fmt"
"maps"
"slices"
"strconv"
"strings"

"github.com/urfave/cli"

"github.com/smartcontractkit/chainlink/v2/core/services/relay"
"github.com/smartcontractkit/chainlink/v2/core/web/presenters"
)

var chainHeaders = []string{"ID", "Enabled", "Config"}
Expand Down Expand Up @@ -39,12 +45,12 @@ type chainClient[P TableRenderer] struct {
path string
}

// newChainClient returns a new ChainClient for a particular type of chains.Config.
// NewChainClient returns a new ChainClient for a particular type of chains.Config.
// P is a TableRenderer corresponding to R, and P2 is the slice variant (type P2 []P).
func newChainClient[P TableRenderer](s *Shell, name string) ChainClient {
return &chainClient[P]{
func NewChainClient(s *Shell, network string) ChainClient {
return &chainClient[ChainPresenter]{
Shell: s,
path: "/v2/chains/" + name,
path: "/v2/chains/" + network,
}
}

Expand All @@ -53,3 +59,52 @@ func (cli *chainClient[P]) IndexChains(c *cli.Context) (err error) {
var p P
return cli.getPage(cli.path, c.Int("page"), &p)
}

// ChainPresenter implements TableRenderer for a ChainResource
type ChainPresenter struct {
presenters.ChainResource
}

// ToRow presents the ChainResource as a slice of strings.
func (p *ChainPresenter) ToRow() []string {
return []string{p.GetID(), strconv.FormatBool(p.Enabled), p.Config}
}

// RenderTable implements TableRenderer
// Just renders a single row
func (p ChainPresenter) RenderTable(rt RendererTable) error {
rows := [][]string{}
rows = append(rows, p.ToRow())

renderList(chainHeaders, rows, rt.Writer)

return nil
}

// ChainPresenters implements TableRenderer for a slice of ChainPresenters.
type ChainPresenters []ChainPresenter

// RenderTable implements TableRenderer
func (ps ChainPresenters) RenderTable(rt RendererTable) error {
rows := [][]string{}

for _, p := range ps {
rows = append(rows, p.ToRow())
}

renderList(chainHeaders, rows, rt.Writer)

return nil
}

func initChainSubCmds(s *Shell) []cli.Command {
cmds := []cli.Command{}
for _, network := range slices.Sorted(maps.Keys(relay.SupportedNetworks)) {
if network == relay.NetworkDummy {
continue
}
cmds = append(cmds, chainCommand(network, NewChainClient(s, network), cli.StringFlag{Name: "id", Usage: "chain ID"}))
}

return cmds
}
81 changes: 81 additions & 0 deletions core/cmd/chains_commands_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package cmd_test

import (
"strconv"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

coscfg "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/config"
solcfg "github.com/smartcontractkit/chainlink-solana/pkg/solana/config"

client2 "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client"
"github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big"
"github.com/smartcontractkit/chainlink/v2/core/cmd"
"github.com/smartcontractkit/chainlink/v2/core/internal/cltest"
"github.com/smartcontractkit/chainlink/v2/core/internal/testutils"
"github.com/smartcontractkit/chainlink/v2/core/internal/testutils/cosmostest"
"github.com/smartcontractkit/chainlink/v2/core/internal/testutils/solanatest"
"github.com/smartcontractkit/chainlink/v2/core/services/chainlink"
)

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

chainID := cosmostest.RandomChainID()
chain := coscfg.TOMLConfig{
ChainID: ptr(chainID),
Enabled: ptr(true),
}
app := cosmosStartNewApplication(t, &chain)
client, r := app.NewShellAndRenderer()

require.NoError(t, cmd.NewChainClient(client, "cosmos").IndexChains(cltest.EmptyCLIContext()))
chains := *r.Renders[0].(*cmd.ChainPresenters)
require.Len(t, chains, 1)
c := chains[0]
assert.Equal(t, chainID, c.ID)
assertTableRenders(t, r)
}

func newRandChainID() *big.Big {
return big.New(testutils.NewRandomEVMChainID())
}

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

app := startNewApplicationV2(t, func(c *chainlink.Config, s *chainlink.Secrets) {
c.EVM[0].Enabled = ptr(true)
c.EVM[0].NonceAutoSync = ptr(false)
c.EVM[0].BalanceMonitor.Enabled = ptr(false)
})
client, r := app.NewShellAndRenderer()

require.NoError(t, cmd.NewChainClient(client, "evm").IndexChains(cltest.EmptyCLIContext()))
chains := *r.Renders[0].(*cmd.ChainPresenters)
require.Len(t, chains, 1)
c := chains[0]
assert.Equal(t, strconv.Itoa(client2.NullClientChainID), c.ID)
assertTableRenders(t, r)
}

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

id := solanatest.RandomChainID()
cfg := solcfg.TOMLConfig{
ChainID: &id,
Enabled: ptr(true),
}
app := solanaStartNewApplication(t, &cfg)
client, r := app.NewShellAndRenderer()

require.NoError(t, cmd.NewChainClient(client, "solana").IndexChains(cltest.EmptyCLIContext()))
chains := *r.Renders[0].(*cmd.ChainPresenters)
require.Len(t, chains, 1)
c := chains[0]
assert.Equal(t, id, c.ID)
assertTableRenders(t, r)
}
48 changes: 0 additions & 48 deletions core/cmd/cosmos_chains_commands.go

This file was deleted.

33 changes: 0 additions & 33 deletions core/cmd/cosmos_chains_commands_test.go

This file was deleted.

48 changes: 0 additions & 48 deletions core/cmd/evm_chains_commands.go

This file was deleted.

38 changes: 0 additions & 38 deletions core/cmd/evm_chains_commands_test.go

This file was deleted.

4 changes: 2 additions & 2 deletions core/cmd/nodes_commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,10 @@ type nodeClient[P TableRenderer] struct {

// newNodeClient returns a new NodeClient for a particular type of NodeStatus.
// P is a TableRenderer for []types.NodeStatus.
func newNodeClient[P TableRenderer](s *Shell, name string) NodeClient {
func newNodeClient[P TableRenderer](s *Shell, network string) NodeClient {
return &nodeClient[P]{
Shell: s,
path: "/v2/nodes/" + name,
path: "/v2/nodes/" + network,
}
}

Expand Down
2 changes: 1 addition & 1 deletion core/cmd/shell.go
Original file line number Diff line number Diff line change
Expand Up @@ -399,7 +399,7 @@ func takeBackupIfVersionUpgrade(dbUrl url.URL, rootDir string, cfg periodicbacku
}

// Because backups can take a long time we must start a "fake" health report to prevent
//node shutdown because of healthcheck fail/timeout
// node shutdown because of healthcheck fail/timeout
err = databaseBackup.RunBackup(appv.String())
return err
}
Expand Down
Loading

0 comments on commit e26dabb

Please sign in to comment.