Skip to content
Open
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
80 changes: 65 additions & 15 deletions pkg/cmd/p2p.go
Original file line number Diff line number Diff line change
@@ -1,19 +1,29 @@
package cmd

import (
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"strings"
"text/tabwriter"

"connectrpc.com/connect"
pb "github.com/evstack/ev-node/types/pb/evnode/v1"
"github.com/spf13/cobra"
"google.golang.org/protobuf/types/known/emptypb"

rpc "github.com/evstack/ev-node/types/pb/evnode/v1/v1connect"
)

const (
flagOutput = "output"
)

func init() {
NetInfoCmd.Flags().StringP(flagOutput, "o", "text", "Output format (text|json)")
}

// NetInfoCmd returns information about the running node via RPC
var NetInfoCmd = &cobra.Command{
Use: "net-info",
Expand All @@ -24,9 +34,8 @@ var NetInfoCmd = &cobra.Command{
if err != nil {
return fmt.Errorf("error parsing config: %w", err)
}

// Get RPC address from config
rpcAddress := nodeConfig.RPC.Address

if rpcAddress == "" {
return fmt.Errorf("RPC address not found in node configuration")
}
Expand All @@ -36,7 +45,10 @@ var NetInfoCmd = &cobra.Command{
Transport: http.DefaultTransport,
}

baseURL := fmt.Sprintf("http://%s", rpcAddress)
baseURL := rpcAddress
if !strings.HasPrefix(rpcAddress, "http://") && !strings.HasPrefix(rpcAddress, "https://") {
baseURL = "http://" + baseURL
}

// Create P2P client
p2pClient := rpc.NewP2PServiceClient(
Expand All @@ -46,16 +58,33 @@ var NetInfoCmd = &cobra.Command{

// Call GetNetInfo RPC
resp, err := p2pClient.GetNetInfo(
context.Background(),
cmd.Context(),
connect.NewRequest(&emptypb.Empty{}),
)
if err != nil {
return fmt.Errorf("error calling GetNetInfo RPC: %w", err)
return fmt.Errorf("GetNetInfo RPC: %w", err)
}

netInfo := resp.Msg.NetInfo
nodeID := netInfo.Id

peerResp, err := p2pClient.GetPeerInfo(
cmd.Context(),
connect.NewRequest(&emptypb.Empty{}),
)
if err != nil {
return fmt.Errorf("GetPeerInfo RPC: %w", err)
}

outputFormat, err := cmd.Flags().GetString(flagOutput)
if err != nil {
return err
}

if outputFormat == "json" {
return formatJson(cmd.OutOrStdout(), netInfo, peerResp)
}
Comment on lines +83 to +85
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The current implementation falls back to text output for any unsupported output format. It would be more user-friendly to validate the output flag and return an error for unsupported values. A switch statement can make this logic cleaner and more robust.

Additionally, to improve the formatJson function's signature and reduce its coupling to connect types, you can pass peerResp.Msg.Peers directly instead of the whole peerResp object.

Suggested change
if outputFormat == "json" {
return formatJson(cmd.OutOrStdout(), netInfo, peerResp)
}
switch outputFormat {
case "json":
return formatJson(cmd.OutOrStdout(), netInfo, peerResp.Msg.Peers)
case "text":
// valid format, proceed to text output
default:
return fmt.Errorf("unsupported output format: %q, please use 'text' or 'json'", outputFormat)
}


nodeID := netInfo.Id
out := cmd.OutOrStdout()
w := tabwriter.NewWriter(out, 2, 0, 2, ' ', 0)

Expand All @@ -73,14 +102,6 @@ var NetInfoCmd = &cobra.Command{
}

fmt.Fprintf(w, "%s\n", strings.Repeat("-", 50))
// Also get peer information
peerResp, err := p2pClient.GetPeerInfo(
context.Background(),
connect.NewRequest(&emptypb.Empty{}),
)
if err != nil {
return fmt.Errorf("error calling GetPeerInfo RPC: %w", err)
}

// Print connected peers in a table-like format
peerCount := len(peerResp.Msg.Peers)
Expand Down Expand Up @@ -109,3 +130,32 @@ var NetInfoCmd = &cobra.Command{
return nil
},
}

func formatJson(w io.Writer, netInfo *pb.NetInfo, peerResp *connect.Response[pb.GetPeerInfoResponse]) error {
type peerJSON struct {
ID string `json:"id"`
Address string `json:"address"`
}
type netInfoJSON struct {
NodeID string `json:"node_id"`
ListenAddresses []string `json:"listen_addresses"`
Peers []peerJSON `json:"peers"`
}

peers := make([]peerJSON, 0, len(peerResp.Msg.Peers))
for _, peer := range peerResp.Msg.Peers {
peers = append(peers, peerJSON{
ID: peer.Id,
Address: peer.Address,
})
}
Comment on lines +134 to +151
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

To align with the suggested change at the call site, the function signature should be updated to accept []*pb.PeerInfo directly, rather than the *connect.Response wrapper. This makes the function's purpose clearer, decouples it from the connect library, and simplifies testing.

Suggested change
func formatJson(w io.Writer, netInfo *pb.NetInfo, peerResp *connect.Response[pb.GetPeerInfoResponse]) error {
type peerJSON struct {
ID string `json:"id"`
Address string `json:"address"`
}
type netInfoJSON struct {
NodeID string `json:"node_id"`
ListenAddresses []string `json:"listen_addresses"`
Peers []peerJSON `json:"peers"`
}
peers := make([]peerJSON, 0, len(peerResp.Msg.Peers))
for _, peer := range peerResp.Msg.Peers {
peers = append(peers, peerJSON{
ID: peer.Id,
Address: peer.Address,
})
}
func formatJson(w io.Writer, netInfo *pb.NetInfo, peerInfos []*pb.PeerInfo) error {
type peerJSON struct {
ID string `json:"id"`
Address string `json:"address"`
}
type netInfoJSON struct {
NodeID string `json:"node_id"`
ListenAddresses []string `json:"listen_addresses"`
Peers []peerJSON `json:"peers"`
}
peers := make([]peerJSON, 0, len(peerInfos))
for _, peer := range peerInfos {
peers = append(peers, peerJSON{
ID: peer.Id,
Address: peer.Address,
})
}


out := netInfoJSON{
NodeID: netInfo.Id,
ListenAddresses: netInfo.ListenAddresses,
Peers: peers,
}
enc := json.NewEncoder(w)
enc.SetIndent("", " ")
return enc.Encode(out)
}
Loading