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

Refactor commands to use the go-flags package properly. #71

Merged
merged 46 commits into from Dec 25, 2018
Merged

Refactor commands to use the go-flags package properly. #71

merged 46 commits into from Dec 25, 2018

Conversation

barisere
Copy link
Contributor

@barisere barisere commented Dec 19, 2018

Commands and subcommands can now be used in a simpler way.

EDIT: This also fixes #70

@barisere barisere added the enhancement New feature or request label Dec 19, 2018
@itswisdomagain
Copy link
Contributor

itswisdomagain commented Dec 20, 2018

Some observations from preliminary testing:

  • Running dcrcli without a command shows a message that doesn't appear customizable. Compare simplify output of running ./dcrcli without a command #53
  • There's a funny behaviour when using an unknown option/flag. Some examples to illustrate:
  • dcrcli -n (unknown flag) doesn't show any message or perform any action. The program just exits
  • dcrcli -n=abc (unknown option) shows a message to specify a command, ignoring the invalid option passed
  • dcrcli send -n does nothing, while dcrcli send -n=abc finally says something expected: "unknown flag `n'"

I think 1 primary trigger for this refactor is to enable us use command options, can you provide an example to that effect? Use the receive command, let's assume it accepts an "AccountNumber" option, type uint32, short "n", long "account". This option is only useful when executing the receive command. I'm eager to see how this plays out

Once we have those issues ironed out, I'll take a deeper dive into the code

@itswisdomagain itswisdomagain added this to For Review in godcr board Dec 20, 2018
itswisdomagain and others added 6 commits December 20, 2018 11:55
Simplify output of running ./dcrcli without a command
Use upstream go-flags dependency.
Commands and subcommands can now be used in a simpler way.
Use upstream go-flags dependency.
@barisere
Copy link
Contributor Author

Some observations from preliminary testing:

* Running `dcrcli` without a command shows a message that doesn't appear customizable. Compare #53

* There's a funny behaviour when using an unknown option/flag. Some examples to illustrate:

...

Can you check those again? The behaviour has changed.

I think 1 primary trigger for this refactor is to enable us use command options, can you provide an example to that effect? Use the receive command, let's assume it accepts an "AccountNumber" option, type uint32, short "n", long "account". This option is only useful when executing the receive command. I'm eager to see how this plays out

Oops, I used the send command instead. Assume the send command has a sub command, custom, that only prints "Custom send called" and the address passed as a flag. The flag has a short form "-a" and a long form "--address". We create this command as follows.

// file: cli/commands.go

type SendCommand struct {
	Custom CustomSend `command:"custom"`
}

type CustomSend struct {
	Address string `short:"a" long:"address"`
}

func (c CustomSend) Execute(args []string) error {
	fmt.Println("Custom send called")
	fmt.Println("address value:", c.Address)
	return nil
}

We register the command as follows.

// file: cli/app.go

type AppCommands struct {
	Send       SendCommand       `command:"send" description:"send a transaction" subcommands-optional:"optional"`
	// other fields
}

var DcrcliCommands AppCommands

And we parse it as follows.

// file: main.go

cli.Setup(client)
parser := flags.NewParser(&cli.DcrcliCommands, flags.Default)
if _, err := parser.Parse(); err != nil {
	// handle error
}

Below is help output when running the compiled code.

$ ./dcrcli -C ./dcrcli.conf send custom --help
Usage:
  dcrcli [OPTIONS] send custom [custom-OPTIONS]

Application Options:
  -v, --version          Display version information and exit
  -C, --configfile=      Path to configuration file
  -u, --rpcuser=         RPC username
  -p, --rpcpass=         RPC password
  -w, --walletrpcserver= Wallet RPC server to connect to
  -c, --rpccert=         RPC server certificate chain for validation
  -s, --serveraddress=   Address and port of the HTTP server.
      --http             Run in HTTP mode.
      --nodaemontls      Disable TLS

Help Options:
  -h, --help             Show this help message

[custom command options]
      -a, --address=

When we run the custom subcommand without the address flag, the output is as follows.

$ ./dcrcli -C ./dcrcli.conf send custom 
Custom send called
address value:

When we run the custom subcommand with the address flag set, the output is as follows.

$ ./dcrcli -C ./dcrcli.conf send custom --address=TsfDLrRkk9ciUuwfp2b8PawwnukYD7yAjGd
Custom send called
address value: TsfDLrRkk9ciUuwfp2b8PawwnukYD7yAjGd

If we use the flag without providing an argument, the following error is shown.

$ ./dcrcli -C ./dcrcli.conf send custom --address
expected argument for flag `-a, --address'

$ ./dcrcli -C ./dcrcli.conf send custom -a
expected argument for flag `-a, --address'

That's mostly it.

Copy link
Contributor

@itswisdomagain itswisdomagain left a comment

Choose a reason for hiding this comment

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

This looks like great improvement. 1 other immediate cause for concern is this:

  • config.go is not restricted to just cli-related usages, it shouldn't belong in the cli package

I have an idea, want to try it out on your branch and see how it goes

cli/commands.go Outdated Show resolved Hide resolved
main.go Outdated Show resolved Hide resolved
Copy link
Contributor

@itswisdomagain itswisdomagain left a comment

Choose a reason for hiding this comment

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

Feedback from testing, haven't really paid another attention to the code. I believe this PR comes with changes to the behaviour of running dcrcli (i.e. without commands), dcrcli -h and dcrcli <command> -h. Those were scenarios I tested for in getting the feedback below.

A fourth use case I simulated in my head but I know this PR also introduces changes regarding, is command-specific options. According to my simulation, this should work well (for the most part). The exception, a pointed below, is when using a command-specific option when running in cli mode. Since commands are limited to cli mode, command-specific options should throw an error when parsing if the user is running on http mode.

I believe the the identified issues below are fixable. An adjusted version of this PR demonstrates potential ways of fixing some of the identified issues. Reference links are provided in such cases.

main.go Show resolved Hide resolved
main.go Show resolved Hide resolved
main.go Outdated Show resolved Hide resolved
@itswisdomagain
Copy link
Contributor

itswisdomagain commented Dec 23, 2018

We'll need to update the Using dcrcli section of the readme

@itswisdomagain
Copy link
Contributor

itswisdomagain commented Dec 23, 2018

For reference purposes, here are the objectives of (or issues affected by) this PR:

  • command-specific options
    Reference issue: balance command simple and detailed #49, also this comment
    Status: Green (feature fully supported by this PR)
    More info: Once this PR is merged, work on balance command cleanup #62 can continue

  • experimental commands distinction when running dcrcli without commands
    Reference issue: simplify output of running ./dcrcli without a command #53
    Status: Yellow (partly supported)
    More info: Requirement is fully implemented in Simplify output of running ./dcrcli without a command #66, but works quite differently in this PR
    Way forward: Leave as is. A new issue (described here) should be created and fixed in a different PR. Fixing that issue would make it possible for this requirement to be fully implemented.

  • command-specific help
    Reference issue: ./dcrcli help #51, also this comment and this other comment
    Status: Red (support for this feature/requirement is not solid enough)
    More info: This PR does a neat job of introducing command-specific help with the dcrcli <command> -h syntax. But the message displayed is too verbose. It should be more concise, providing "details" about how the command functions and the optional arguments and flags it works with. Similar work being done in [WIP] ./dcrcli help #63 cannot continue with what we have at the moment
    Way forward: Make all commands have long description tags in app.go. [WIP] ./dcrcli help #63 then uses a parser to get the long description for each command and displays that when dcrcli help <command> is issued. dcrcli help in [WIP] ./dcrcli help #63 should produce the same output as dcrcli -h in this PR. Remove support for command-specific help via dcrcli <command> -h in this PR or have dcrcli <command> -h display what it currently displays AND include instruction to use dcrcli help <command> to get detailed help for the command. For this PR, dcrcli help <command> may just print a dummy message awaiting complete implementation in [WIP] ./dcrcli help #63

PS: I haven't reviewed the code yet, this "overview" may take a different outlook after I review the code

EDIT (after code review)
Regarding the way forward for command-specific help, let's leave the code as is, and allow #63 take care of that. I think there's somewhat enough base work to build on, following the idea of adding long description tags to the commands or by implementing Usage interface on each command. A number of options we can explore, let's get this PR merged first and we'll deal with that

Copy link
Contributor

@itswisdomagain itswisdomagain left a comment

Choose a reason for hiding this comment

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

Got around to reviewing the code. Some thoughts...

cli/app.go Outdated Show resolved Hide resolved
cli/app.go Outdated Show resolved Hide resolved
cli/cli.go Outdated
client.registerHandlers()

return client
type response struct {
Copy link
Contributor

Choose a reason for hiding this comment

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

Permit me to saddle you with this requirement. This type should go. All commands should handle displaying appropriate responses after successful execution. Compare this comment. You may not want to stress too much about how to display the successful output for different commands, just print what is currently printed. Other PRs building off related issues will revise the successful message printing for each command. Examples are #49, #54 and #55

As provided for by the signature of Execute in goflags.Commander, commands should only return an error which is printed to the terminal.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think this should be done in a different PR. Take the balance handler: #49 will change the way the output is displayed. Same for the others. Since they're working on the output messages also, we can let them do it, or do it ourselves in a separate PR. Doing it now will require that I change the output myself (even if I do not handle formatting, I get to unnecessarily mess with what they're doing).

Copy link
Contributor

Choose a reason for hiding this comment

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

Okay

cli/commands.go Outdated
type SendCommand struct{}

func (s SendCommand) Execute(args []string) error {
res, err := normalSend(walletClient, args)
Copy link
Contributor

Choose a reason for hiding this comment

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

As mentioned in an earlier comment, the code in normalSend should come here. No need to call another handler function, this particular pattern is repeated across all Execute methods in this file, causing avoidable repetition.

Also, assuming SendCommand supports some command-specific options, we can have access to such option here seamlessly (s.option). Passing such options to the normalSend handler will be another stress that can be avoided.

Copy link
Contributor

Choose a reason for hiding this comment

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

This comment applies to all Execute methods in this file. Also, it might make sense to put these in handlers.go, leaving commands.go as a file that describes the supported commands and handler.go as the file that actually perform the actions for each command.

cli/core/config.go Outdated Show resolved Hide resolved
cli/core/config.go Outdated Show resolved Hide resolved
main.go Outdated Show resolved Hide resolved
main.go Outdated Show resolved Hide resolved
main.go Outdated Show resolved Hide resolved
main.go Outdated Show resolved Hide resolved
@barisere barisere mentioned this pull request Dec 24, 2018
Copy link
Contributor

@itswisdomagain itswisdomagain left a comment

Choose a reason for hiding this comment

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

We're making progress. Just some code cleanups

cli/commands/app.go Outdated Show resolved Hide resolved
cli/commands/history.go Outdated Show resolved Hide resolved
cli/commands/balance.go Outdated Show resolved Hide resolved
cli/commands/history.go Outdated Show resolved Hide resolved
cli/commands/receive.go Outdated Show resolved Hide resolved
main.go Outdated Show resolved Hide resolved
main.go Outdated Show resolved Hide resolved
main.go Outdated
_, err := parser.Parse()
if cli.IsFlagErrorType(err, flags.ErrCommandRequired) {
commands := supportedCommands(parser)
fmt.Fprintln(os.Stderr, "Available Commands: ", strings.Join(commands, ", "))
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we make supportedCommands return the joined string instead? The function isn't really used anywhere else so we can modify it to return what we actually need rather than it returning something and then we perform some other operation on the returned value.

Alternatively, we can just modify the supportedCommands function to print out the message and return nothing. The command can also be renamed accordingly.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Supported commands are a list of commands, so a []string is appropriate.

Copy link
Contributor

Choose a reason for hiding this comment

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

The motivation for this requirement is that supportedCommands isn't used or needed anywhere else. However, the function would likely find use soon (in #63) so let's leave it as is

cli/cli.go Outdated Show resolved Hide resolved
walletrpcclient/walletrpcclient.go Show resolved Hide resolved
@barisere
Copy link
Contributor Author

barisere commented Dec 24, 2018

@itswisdomagain The help message for command-specific help has been shortened. Here's what it looks like now.

$ ./dcrcli -C dcrcli.conf send -h
Usage:
  dcrcli [OPTIONS] send
$ ./dcrcli -C dcrcli.conf receive -h
Usage:
  dcrcli [OPTIONS] receive [account]

If that is acceptable, then command-specific help has been covered as required, leaving us with experimental commands to grok.

@itswisdomagain
Copy link
Contributor

@itswisdomagain The help message for command-specific help has been shortened. Here's what it looks like now.

$ ./dcrcli -C dcrcli.conf send -h
Usage:
  dcrcli [OPTIONS] send
$ ./dcrcli -C dcrcli.conf receive -h
Usage:
  dcrcli [OPTIONS] receive [account]

To a very large extent, it does. The associated issue #51 recommended creating a new command dcrcli help to do what dcrcli -h does and dcrcli help <command> to do what dcrcli <command> -h does now.

Let's retain this, it's a welcome change. We might need to clean it up (I'm thinking, add a message below the command usage text that says how to see the options, something like use dcrcli -h to view command options), but we'll have to discuss this with @raedah and then PR #63 can continue from there.

main.go Outdated
fmt.Println(err)
return
}
if parser.Active == nil {
Copy link
Contributor

Choose a reason for hiding this comment

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

If you use else if here, the return in the preceding if block would be unnecessary

os.Exit(1) only if there command run returned an error.
os.Exit(1) only if the command run returned an error.
@itswisdomagain itswisdomagain merged commit 0688516 into raedahgroup:master Dec 25, 2018
@itswisdomagain itswisdomagain moved this from For Review to Done in godcr board Dec 25, 2018
itswisdomagain added a commit to itswisdomagain/godcr that referenced this pull request Dec 29, 2018
Refactor commands to use the go-flags package properly.
@barisere barisere deleted the provide-command-types branch January 10, 2019 04:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
Development

Successfully merging this pull request may close these issues.

Error parsing config file output
3 participants