Merge PR #2062: Support a proposal JSON file in submit-proposal

This commit is contained in:
Christopher Goes 2018-08-22 12:31:58 +02:00 committed by GitHub
commit 4b5bb45a09
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 163 additions and 7 deletions

View File

@ -29,6 +29,7 @@ FEATURES
* Gaia CLI (`gaiacli`) * Gaia CLI (`gaiacli`)
* [cli] Cmds to query staking pool and params * [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 * Gaia

View File

@ -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 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: Get the details of your newly created proposal:
```bash ```bash

View File

@ -15,6 +15,9 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/viper" "github.com/spf13/viper"
"io/ioutil"
"encoding/json"
"strings"
) )
const ( const (
@ -28,18 +31,51 @@ const (
flagDepositer = "depositer" flagDepositer = "depositer"
flagStatus = "status" flagStatus = "status"
flagLatestProposalIDs = "latest" 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. // GetCmdSubmitProposal implements submitting a proposal transaction command.
func GetCmdSubmitProposal(cdc *wire.Codec) *cobra.Command { func GetCmdSubmitProposal(cdc *wire.Codec) *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "submit-proposal", Use: "submit-proposal",
Short: "Submit a proposal along with an initial deposit", 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 { RunE: func(cmd *cobra.Command, args []string) error {
title := viper.GetString(flagTitle) proposal, err := parseSubmitProposalFlags()
description := viper.GetString(flagDescription) if err != nil {
strProposalType := viper.GetString(flagProposalType) return err
initialDeposit := viper.GetString(flagDeposit) }
txCtx := authctx.NewTxContextFromCLI().WithCodec(cdc) txCtx := authctx.NewTxContextFromCLI().WithCodec(cdc)
cliCtx := context.NewCLIContext(). cliCtx := context.NewCLIContext().
@ -52,17 +88,17 @@ func GetCmdSubmitProposal(cdc *wire.Codec) *cobra.Command {
return err return err
} }
amount, err := sdk.ParseCoins(initialDeposit) amount, err := sdk.ParseCoins(proposal.Deposit)
if err != nil { if err != nil {
return err return err
} }
proposalType, err := gov.ProposalTypeFromString(strProposalType) proposalType, err := gov.ProposalTypeFromString(proposal.Type)
if err != nil { if err != nil {
return err return err
} }
msg := gov.NewMsgSubmitProposal(title, description, proposalType, fromAddr, amount) msg := gov.NewMsgSubmitProposal(proposal.Title, proposal.Description, proposalType, fromAddr, amount)
err = msg.ValidateBasic() err = msg.ValidateBasic()
if err != nil { if err != nil {
@ -80,10 +116,42 @@ func GetCmdSubmitProposal(cdc *wire.Codec) *cobra.Command {
cmd.Flags().String(flagDescription, "", "description of proposal") cmd.Flags().String(flagDescription, "", "description of proposal")
cmd.Flags().String(flagProposalType, "", "proposalType of proposal") cmd.Flags().String(flagProposalType, "", "proposalType of proposal")
cmd.Flags().String(flagDeposit, "", "deposit 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 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. // GetCmdDeposit implements depositing tokens for an active proposal.
func GetCmdDeposit(cdc *wire.Codec) *cobra.Command { func GetCmdDeposit(cdc *wire.Codec) *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{

View File

@ -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")
}