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

WIP: CLI Response Formatting #3275

Closed
wants to merge 2 commits into from
Closed
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
34 changes: 34 additions & 0 deletions client/output.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package client

import (
"fmt"

"github.com/cosmos/cosmos-sdk/codec"
"github.com/spf13/viper"
"github.com/tendermint/tendermint/libs/cli"
)

// Printable defines which structs can be printed by
// CLI output functions
type Printable interface {
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't see why we're implementing a new interface. This is what Stringer is for 👍

Copy link
Member Author

@jackzampolin jackzampolin Jan 11, 2019

Choose a reason for hiding this comment

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

Duh, I'll make that fix. Will be easier 🤦‍♂️

HumanReadableString() string
}

// PrintOutput prints output while respecting output and indent flags
// NOTE: pass in marshalled structs that have been unmarshalled
func PrintOutput(cdc *codec.Codec, toPrint Printable) {
switch viper.Get(cli.OutputFlag) {
case "text":
fmt.Println(toPrint.HumanReadableString())
case "json":
if viper.GetBool(FlagIndentResponse) {
out, err := codec.MarshalJSONIndent(cdc, toPrint)
if err != nil {
panic(err)
}
fmt.Println(string(out))
} else {
fmt.Println(string(cdc.MustMarshalJSON(toPrint)))
}
}
}
1 change: 1 addition & 0 deletions client/tx/search.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ $ gaiacli query txs --tags '<tag1>:<value1>&<tag2>:<value2>'
cmd.Flags().Bool(client.FlagTrustNode, false, "Trust connected full node (don't verify proofs for responses)")
viper.BindPFlag(client.FlagTrustNode, cmd.Flags().Lookup(client.FlagTrustNode))
cmd.Flags().String(flagTags, "", "tag:value list of tags that must match")
cmd.MarkFlagRequired(flagTags)
return cmd
}

Expand Down
104 changes: 84 additions & 20 deletions x/gov/client/cli/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/spf13/cobra"
"github.com/spf13/viper"

"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/context"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
Expand Down Expand Up @@ -41,7 +42,9 @@ $ gaiacli query gov proposal 1
return err
}

fmt.Println(string(res))
var proposal gov.Proposal
cdc.UnmarshalJSON(res, &proposal)
client.PrintOutput(cdc, proposal)
return nil
},
}
Expand Down Expand Up @@ -125,21 +128,17 @@ $ gaiacli query gov proposals --status (DepositPeriod|VotingPeriod|Passed|Reject
return err
}

var matchingProposals []gov.Proposal
var matchingProposals gov.Proposals
err = cdc.UnmarshalJSON(res, &matchingProposals)
if err != nil {
return err
}

if len(matchingProposals) == 0 {
fmt.Println("No matching proposals found")
return nil
}

for _, proposal := range matchingProposals {
fmt.Printf(" %d - %s\n", proposal.GetProposalID(), proposal.GetTitle())
return fmt.Errorf("No matching proposals found")
}

client.PrintOutput(cdc, matchingProposals)
return nil
},
}
Expand Down Expand Up @@ -206,7 +205,12 @@ $ gaiacli query gov vote 1 cosmos1skjwj5whet0lpe65qaq4rpq03hjxlwd9nf39lk
}
}

fmt.Println(string(res))
cdc.UnmarshalJSON(res, &vote)
if vote.Empty() {
return fmt.Errorf("Unable to find vote for proposal %d from voter %s", proposalID, voterAddr)
}

client.PrintOutput(cdc, vote)
return nil
},
}
Expand Down Expand Up @@ -263,7 +267,9 @@ $ gaiacli query gov votes 1
return err
}

fmt.Println(string(res))
var votes gov.Votes
cdc.MustUnmarshalJSON(res, &votes)
client.PrintOutput(cdc, votes)
return nil
},
}
Expand Down Expand Up @@ -317,15 +323,19 @@ $ gaiacli query gov deposit 1 cosmos1skjwj5whet0lpe65qaq4rpq03hjxlwd9nf39lk

var deposit gov.Deposit
cdc.UnmarshalJSON(res, &deposit)

if deposit.Empty() {
res, err = gcutils.QueryDepositByTxQuery(cdc, cliCtx, params)
if err != nil {
return err
}
}

fmt.Println(string(res))
cdc.UnmarshalJSON(res, &deposit)
if deposit.Empty() {
return fmt.Errorf("Unable to find deposit for proposal %d from depositor %s", proposalID, depositorAddr)
}

client.PrintOutput(cdc, deposit)
return nil
},
}
Expand Down Expand Up @@ -381,7 +391,9 @@ $ gaiacli query gov deposits 1
return err
}

fmt.Println(string(res))
var deposits gov.Deposits
cdc.MustUnmarshalJSON(res, &deposits)
client.PrintOutput(cdc, deposits)
return nil
},
}
Expand Down Expand Up @@ -428,16 +440,52 @@ $ gaiacli query gov tally 1
return err
}

fmt.Println(string(res))
var tally gov.TallyResult
cdc.MustUnmarshalJSON(res, &tally)
client.PrintOutput(cdc, tally)
return nil
},
}

return cmd
}

// GetCmdQueryProposal implements the query proposal command.
// GetCmdQueryParams returns a cli command to query all goverance params
func GetCmdQueryParams(queryRoute string, cdc *codec.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "params",
Short: "Query all parameters of the governance process",
RunE: func(cmd *cobra.Command, args []string) error {
cliCtx := context.NewCLIContext().WithCodec(cdc)
tp, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/params/tallying", queryRoute), nil)
if err != nil {
return err
}
dp, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/params/deposit", queryRoute), nil)
if err != nil {
return err
}
vp, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/params/voting", queryRoute), nil)
if err != nil {
return err
}

var tallyParams gov.TallyParams
cdc.MustUnmarshalJSON(tp, &tallyParams)
var depositParams gov.DepositParams
cdc.MustUnmarshalJSON(dp, &depositParams)
var votingParams gov.VotingParams
cdc.MustUnmarshalJSON(vp, &votingParams)

client.PrintOutput(cdc, gov.AllGovParams{depositParams, tallyParams, votingParams})
return nil
},
}
return cmd
}

// GetCmdQueryProposal implements the query proposal command.
func GetCmdQueryParam(queryRoute string, cdc *codec.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "param [param-type]",
Args: cobra.ExactArgs(1),
Expand All @@ -450,12 +498,28 @@ func GetCmdQueryParams(queryRoute string, cdc *codec.Codec) *cobra.Command {
if err != nil {
return err
}

fmt.Println(string(res))
var out gov.GovParams
switch args[0] {
case "voting":
var param gov.VotingParams
cdc.MustUnmarshalJSON(res, &param)
out = param
case "tallying":
var param gov.TallyParams
cdc.MustUnmarshalJSON(res, &param)
out = param
case "deposit":
var param gov.DepositParams
cdc.MustUnmarshalJSON(res, &param)
out = param
default:
return fmt.Errorf("Arguement must be one of (voting|tallying|deposit), was %s", args[0])
}

client.PrintOutput(cdc, out)
return nil
},
}

return cmd
}

Expand All @@ -474,12 +538,12 @@ func GetCmdQueryProposer(queryRoute string, cdc *codec.Codec) *cobra.Command {
return fmt.Errorf("proposal-id %s is not a valid uint", args[0])
}

res, err := gcutils.QueryProposerByTxQuery(cdc, cliCtx, proposalID)
proposor, err := gcutils.QueryProposerByTxQuery(cdc, cliCtx, proposalID)
if err != nil {
return err
}

fmt.Println(string(res))
client.PrintOutput(cdc, proposor)
return nil
},
}
Expand Down
1 change: 1 addition & 0 deletions x/gov/client/module_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ func (mc ModuleClient) GetQueryCmd() *cobra.Command {
govCli.GetCmdQueryProposals(mc.storeKey, mc.cdc),
govCli.GetCmdQueryVote(mc.storeKey, mc.cdc),
govCli.GetCmdQueryVotes(mc.storeKey, mc.cdc),
govCli.GetCmdQueryParam(mc.storeKey, mc.cdc),
govCli.GetCmdQueryParams(mc.storeKey, mc.cdc),
govCli.GetCmdQueryProposer(mc.storeKey, mc.cdc),
govCli.GetCmdQueryDeposit(mc.storeKey, mc.cdc),
Expand Down
25 changes: 12 additions & 13 deletions x/gov/client/utils/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,13 @@ import (
// Proposer contains metadata of a governance proposal used for querying a
// proposer.
type Proposer struct {
ProposalID uint64 `json:"proposal_id"`
Proposer string `json:"proposer"`
ProposalID uint64 `json:"proposal_id,omitempty"`
Proposer string `json:"proposer,omitempty"`
}

// HumanReadableOutput statisfies client.Printable
func (p Proposer) HumanReadableString() string {
return fmt.Sprintf(`%s is the proposer for proposal %d...`, p.Proposer, p.ProposalID)
}

// QueryDepositsByTxQuery will query for deposits via a direct txs tags query. It
Expand Down Expand Up @@ -188,7 +193,7 @@ func QueryDepositByTxQuery(
// ID.
func QueryProposerByTxQuery(
cdc *codec.Codec, cliCtx context.CLIContext, proposalID uint64,
) ([]byte, error) {
) (Proposer, error) {

tags := []string{
fmt.Sprintf("%s='%s'", tags.Action, tags.ActionProposalSubmitted),
Expand All @@ -197,7 +202,7 @@ func QueryProposerByTxQuery(

infos, err := tx.SearchTxs(cliCtx, cdc, tags)
if err != nil {
return nil, err
return Proposer{}, err
}

for _, info := range infos {
Expand All @@ -206,19 +211,13 @@ func QueryProposerByTxQuery(
if msg.Type() == gov.TypeMsgSubmitProposal {
subMsg := msg.(gov.MsgSubmitProposal)

proposer := Proposer{
return Proposer{
ProposalID: proposalID,
Proposer: subMsg.Proposer.String(),
}

if cliCtx.Indent {
return cdc.MarshalJSONIndent(proposer, "", " ")
}

return cdc.MarshalJSON(proposer)
}, nil
}
}
}

return nil, fmt.Errorf("failed to find the proposer for proposalID %d", proposalID)
return Proposer{}, fmt.Errorf("failed to find the proposer for proposalID %d", proposalID)
}
59 changes: 49 additions & 10 deletions x/gov/depositsvotes.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package gov
import (
"encoding/json"
"fmt"
"strings"

"github.com/pkg/errors"

Expand All @@ -16,15 +17,34 @@ type Vote struct {
Option VoteOption `json:"option"` // option from OptionSet chosen by the voter
}

// HumanReadableString implements client.Printable
func (v Vote) HumanReadableString() string {
return fmt.Sprintf("%s voted with option %s for proposal %d...", v.Voter, v.Option, v.ProposalID)
}

// Votes is an array of vote
type Votes []Vote

// HumanReadableString implements client.Printable
func (v Votes) HumanReadableString() (out string) {
if len(v) < 1 {
return ""
}
out += fmt.Sprintf("Votes for Proposal %d:\n", v[0].ProposalID)
for _, vot := range v {
out += fmt.Sprintf(" %s: %s\n", vot.Voter, vot.Option)
}
return strings.TrimSpace(out)
}

// Returns whether 2 votes are equal
func (voteA Vote) Equals(voteB Vote) bool {
return voteA.Voter.Equals(voteB.Voter) && voteA.ProposalID == voteB.ProposalID && voteA.Option == voteB.Option
func (v Vote) Equals(comp Vote) bool {
return v.Voter.Equals(comp.Voter) && v.ProposalID == comp.ProposalID && v.Option == comp.Option
}

// Returns whether a vote is empty
func (voteA Vote) Empty() bool {
voteB := Vote{}
return voteA.Equals(voteB)
func (v Vote) Empty() bool {
return v.Equals(Vote{})
}

// Deposit
Expand All @@ -34,15 +54,34 @@ type Deposit struct {
Amount sdk.Coins `json:"amount"` // Deposit amount
}

// HumanReadableString implements client.Printable
func (d Deposit) HumanReadableString() string {
return fmt.Sprintf("%s deposited %s on proposal %d...", d.Depositor, d.Amount, d.ProposalID)
}

// Deposits is a collection of deposit
type Deposits []Deposit

// HumanReadableString implements client.Printable
func (d Deposits) HumanReadableString() (out string) {
if len(d) < 1 {
return ""
}
out += fmt.Sprintf("Deposits for Proposal %d:\n", d[0].ProposalID)
for _, dep := range d {
out += fmt.Sprintf(" %s: %s\n", dep.Depositor, dep.Amount)
}
return strings.TrimSpace(out)
}

// Returns whether 2 deposits are equal
func (depositA Deposit) Equals(depositB Deposit) bool {
return depositA.Depositor.Equals(depositB.Depositor) && depositA.ProposalID == depositB.ProposalID && depositA.Amount.IsEqual(depositB.Amount)
func (d Deposit) Equals(comp Deposit) bool {
return d.Depositor.Equals(comp.Depositor) && d.ProposalID == comp.ProposalID && d.Amount.IsEqual(comp.Amount)
}

// Returns whether a deposit is empty
func (depositA Deposit) Empty() bool {
depositB := Deposit{}
return depositA.Equals(depositB)
func (d Deposit) Empty() bool {
return d.Equals(Deposit{})
}

// Type that represents VoteOption as a byte
Expand Down
Loading