Merge PR #2062: Support a proposal JSON file in submit-proposal
This commit is contained in:
commit
4b5bb45a09
|
@ -29,6 +29,7 @@ FEATURES
|
|||
|
||||
* Gaia CLI (`gaiacli`)
|
||||
* [cli] Cmds to query staking pool and params
|
||||
* [gov][cli] #2062 added `--proposal` flag to `submit-proposal` that allows a JSON file containing a proposal to be passed in
|
||||
|
||||
* Gaia
|
||||
|
||||
|
|
|
@ -6,6 +6,23 @@ Uuse the CLI to create a new proposal:
|
|||
simplegovcli propose --title="Voting Period update" --description="Should we change the proposal voting period to 3 weeks?" --deposit=300Atoms
|
||||
```
|
||||
|
||||
Or, via a json file:
|
||||
|
||||
```bash
|
||||
simplegovcli propose --proposal="path/to/proposal.json"
|
||||
```
|
||||
|
||||
Where proposal.json contains:
|
||||
|
||||
```json
|
||||
{
|
||||
"title": "Voting Period Update",
|
||||
"description": "Should we change the proposal voting period to 3 weeks?",
|
||||
"type": "Text",
|
||||
"deposit": "300Atoms"
|
||||
}
|
||||
```
|
||||
|
||||
Get the details of your newly created proposal:
|
||||
|
||||
```bash
|
||||
|
|
|
@ -15,6 +15,9 @@ import (
|
|||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
"io/ioutil"
|
||||
"encoding/json"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -28,18 +31,51 @@ const (
|
|||
flagDepositer = "depositer"
|
||||
flagStatus = "status"
|
||||
flagLatestProposalIDs = "latest"
|
||||
flagProposal = "proposal"
|
||||
)
|
||||
|
||||
type proposal struct {
|
||||
Title string
|
||||
Description string
|
||||
Type string
|
||||
Deposit string
|
||||
}
|
||||
|
||||
var proposalFlags = []string{
|
||||
flagTitle,
|
||||
flagDescription,
|
||||
flagProposalType,
|
||||
flagDeposit,
|
||||
}
|
||||
|
||||
// GetCmdSubmitProposal implements submitting a proposal transaction command.
|
||||
func GetCmdSubmitProposal(cdc *wire.Codec) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "submit-proposal",
|
||||
Short: "Submit a proposal along with an initial deposit",
|
||||
Long: strings.TrimSpace(`
|
||||
Submit a proposal along with an initial deposit. Proposal title, description, type and deposit can be given directly or through a proposal JSON file. For example:
|
||||
|
||||
$ gaiacli gov submit-proposal --proposal="path/to/proposal.json"
|
||||
|
||||
where proposal.json contains:
|
||||
|
||||
{
|
||||
"title": "Test Proposal",
|
||||
"description": "My awesome proposal",
|
||||
"type": "Text",
|
||||
"deposit": "1000test"
|
||||
}
|
||||
|
||||
is equivalent to
|
||||
|
||||
$ gaiacli gov submit-proposal --title="Test Proposal" --description="My awesome proposal" --type="Text" --deposit="1000test"
|
||||
`),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
title := viper.GetString(flagTitle)
|
||||
description := viper.GetString(flagDescription)
|
||||
strProposalType := viper.GetString(flagProposalType)
|
||||
initialDeposit := viper.GetString(flagDeposit)
|
||||
proposal, err := parseSubmitProposalFlags()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
txCtx := authctx.NewTxContextFromCLI().WithCodec(cdc)
|
||||
cliCtx := context.NewCLIContext().
|
||||
|
@ -52,17 +88,17 @@ func GetCmdSubmitProposal(cdc *wire.Codec) *cobra.Command {
|
|||
return err
|
||||
}
|
||||
|
||||
amount, err := sdk.ParseCoins(initialDeposit)
|
||||
amount, err := sdk.ParseCoins(proposal.Deposit)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
proposalType, err := gov.ProposalTypeFromString(strProposalType)
|
||||
proposalType, err := gov.ProposalTypeFromString(proposal.Type)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msg := gov.NewMsgSubmitProposal(title, description, proposalType, fromAddr, amount)
|
||||
msg := gov.NewMsgSubmitProposal(proposal.Title, proposal.Description, proposalType, fromAddr, amount)
|
||||
|
||||
err = msg.ValidateBasic()
|
||||
if err != nil {
|
||||
|
@ -80,10 +116,42 @@ func GetCmdSubmitProposal(cdc *wire.Codec) *cobra.Command {
|
|||
cmd.Flags().String(flagDescription, "", "description of proposal")
|
||||
cmd.Flags().String(flagProposalType, "", "proposalType of proposal")
|
||||
cmd.Flags().String(flagDeposit, "", "deposit of proposal")
|
||||
cmd.Flags().String(flagProposal, "", "proposal file path (if this path is given, other proposal flags are ignored)")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func parseSubmitProposalFlags() (*proposal, error) {
|
||||
proposal := &proposal{}
|
||||
proposalFile := viper.GetString(flagProposal)
|
||||
|
||||
if proposalFile == "" {
|
||||
proposal.Title = viper.GetString(flagTitle)
|
||||
proposal.Description = viper.GetString(flagDescription)
|
||||
proposal.Type = viper.GetString(flagProposalType)
|
||||
proposal.Deposit = viper.GetString(flagDeposit)
|
||||
return proposal, nil
|
||||
}
|
||||
|
||||
for _, flag := range proposalFlags {
|
||||
if viper.GetString(flag) != "" {
|
||||
return nil, fmt.Errorf("--%s flag provided alongside --proposal, which is a noop", flag)
|
||||
}
|
||||
}
|
||||
|
||||
contents, err := ioutil.ReadFile(proposalFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = json.Unmarshal(contents, proposal)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return proposal, nil
|
||||
}
|
||||
|
||||
// GetCmdDeposit implements depositing tokens for an active proposal.
|
||||
func GetCmdDeposit(cdc *wire.Codec) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
package cli
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"github.com/spf13/viper"
|
||||
"io/ioutil"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestParseSubmitProposalFlags(t *testing.T) {
|
||||
okJSON, err := ioutil.TempFile("", "proposal")
|
||||
require.Nil(t, err, "unexpected error")
|
||||
okJSON.WriteString(`
|
||||
{
|
||||
"title": "Test Proposal",
|
||||
"description": "My awesome proposal",
|
||||
"type": "Text",
|
||||
"deposit": "1000test"
|
||||
}
|
||||
`)
|
||||
|
||||
badJSON, err := ioutil.TempFile("", "proposal")
|
||||
require.Nil(t, err, "unexpected error")
|
||||
badJSON.WriteString("bad json")
|
||||
|
||||
// nonexistent json
|
||||
viper.Set(flagProposal, "fileDoesNotExist")
|
||||
_, err = parseSubmitProposalFlags()
|
||||
require.Error(t, err)
|
||||
|
||||
// invalid json
|
||||
viper.Set(flagProposal, badJSON.Name())
|
||||
_, err = parseSubmitProposalFlags()
|
||||
require.Error(t, err)
|
||||
|
||||
// ok json
|
||||
viper.Set(flagProposal, okJSON.Name())
|
||||
proposal1, err := parseSubmitProposalFlags()
|
||||
require.Nil(t, err, "unexpected error")
|
||||
require.Equal(t, "Test Proposal", proposal1.Title)
|
||||
require.Equal(t, "My awesome proposal", proposal1.Description)
|
||||
require.Equal(t, "Text", proposal1.Type)
|
||||
require.Equal(t, "1000test", proposal1.Deposit)
|
||||
|
||||
// flags that can't be used with --proposal
|
||||
for _, incompatibleFlag := range proposalFlags {
|
||||
viper.Set(incompatibleFlag, "some value")
|
||||
_, err := parseSubmitProposalFlags()
|
||||
require.Error(t, err)
|
||||
viper.Set(incompatibleFlag, "")
|
||||
}
|
||||
|
||||
// no --proposal, only flags
|
||||
viper.Set(flagProposal, "")
|
||||
viper.Set(flagTitle, proposal1.Title)
|
||||
viper.Set(flagDescription, proposal1.Description)
|
||||
viper.Set(flagProposalType, proposal1.Type)
|
||||
viper.Set(flagDeposit, proposal1.Deposit)
|
||||
proposal2, err := parseSubmitProposalFlags()
|
||||
require.Nil(t, err, "unexpected error")
|
||||
require.Equal(t, proposal1.Title, proposal2.Title)
|
||||
require.Equal(t, proposal1.Description, proposal2.Description)
|
||||
require.Equal(t, proposal1.Type, proposal2.Type)
|
||||
require.Equal(t, proposal1.Deposit, proposal2.Deposit)
|
||||
|
||||
err = okJSON.Close()
|
||||
require.Nil(t, err, "unexpected error")
|
||||
err = badJSON.Close()
|
||||
require.Nil(t, err, "unexpected error")
|
||||
}
|
Loading…
Reference in New Issue