Skip to content

Commit

Permalink
Support sorting member lags by value (#39)
Browse files Browse the repository at this point in the history
* Update cli parsing

* Update command parsing

* Check flags more strictly

* Add checkArgs tests

* Bump version
  • Loading branch information
yolken-segment authored Jul 9, 2021
1 parent 5936120 commit d7f7c32
Show file tree
Hide file tree
Showing 6 changed files with 237 additions and 58 deletions.
15 changes: 14 additions & 1 deletion cmd/topicctl/subcmd/get.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ var getCmd = &cobra.Command{
type getCmdConfig struct {
clusterConfig string
full bool
sortValues bool
zkAddr string
zkPrefix string
}
Expand All @@ -54,6 +55,12 @@ func init() {
false,
"Show more full information for resources",
)
getCmd.Flags().BoolVar(
&getConfig.sortValues,
"sort-values",
false,
"Sort by value instead of name; only applies for lags at the moment",
)
getCmd.Flags().StringVarP(
&getConfig.zkAddr,
"zk-addr",
Expand Down Expand Up @@ -153,7 +160,13 @@ func getRun(cmd *cobra.Command, args []string) error {
return fmt.Errorf("Must provide topic and groupID as additional positional arguments")
}

return cliRunner.GetMemberLags(ctx, args[1], args[2], getConfig.full)
return cliRunner.GetMemberLags(
ctx,
args[1],
args[2],
getConfig.full,
getConfig.sortValues,
)
case "members":
if len(args) != 2 {
return fmt.Errorf("Must provide group ID as second positional argument")
Expand Down
13 changes: 12 additions & 1 deletion pkg/cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"os"
"path/filepath"
"regexp"
"sort"
"strconv"
"strings"
"time"
Expand Down Expand Up @@ -357,6 +358,7 @@ func (c *CLIRunner) GetMemberLags(
topic string,
groupID string,
full bool,
sortByValues bool,
) error {
c.startSpinner()

Expand All @@ -375,7 +377,16 @@ func (c *CLIRunner) GetMemberLags(
return err
}

c.printer("Group member lags:\n%s", groups.FormatMemberLags(memberLags, full))
if sortByValues {
sort.Slice(memberLags, func(a, b int) bool {
return memberLags[a].TimeLag() < memberLags[b].TimeLag()
})
}

c.printer(
"Group member lags:\n%s",
groups.FormatMemberLags(memberLags, full),
)
return nil
}

Expand Down
79 changes: 79 additions & 0 deletions pkg/cli/command.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package cli

import (
"fmt"
"strings"
)

type replCommand struct {
args []string
flags map[string]string
}

func (r replCommand) getBoolValue(key string) bool {
value, ok := r.flags[key]

if value == "true" {
return true
} else if value == "" && ok {
// If key is set but value is not, treat this as "true"
return true
} else {
return false
}
}

func (r replCommand) checkArgs(
minArgs int,
maxArgs int,
allowedFlags map[string]struct{},
) error {
if minArgs == maxArgs {
if len(r.args) != minArgs {
return fmt.Errorf("Expected %d args", minArgs)
}
} else {
if len(r.args) < minArgs || len(r.args) > maxArgs {
return fmt.Errorf("Expected between %d and %d args", minArgs, maxArgs)
}
}

for key := range r.flags {
if allowedFlags == nil {
return fmt.Errorf("Flag %s not recognized", key)
}
if _, ok := allowedFlags[key]; !ok {
return fmt.Errorf("Flag %s not recognized", key)
}
}

return nil
}

func parseReplInputs(input string) replCommand {
args := []string{}
flags := map[string]string{}

components := strings.Split(input, " ")

for c, component := range components {
if component == "" {
continue
} else if c > 0 && strings.HasPrefix(component, "--") {
subcomponents := strings.SplitN(component, "=", 2)
key := subcomponents[0][2:]
var value string
if len(subcomponents) > 1 {
value = subcomponents[1]
}
flags[key] = value
} else {
args = append(args, component)
}
}

return replCommand{
args: args,
flags: flags,
}
}
71 changes: 71 additions & 0 deletions pkg/cli/command_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package cli

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestParseReplInputs(t *testing.T) {
assert.Equal(
t,
replCommand{
args: []string{"arg1", "arg2"},
flags: map[string]string{},
},
parseReplInputs("arg1 arg2"),
)
assert.Equal(
t,
replCommand{
args: []string{"--flag1=value1", "arg1", "arg2"},
flags: map[string]string{},
},
parseReplInputs("--flag1=value1 arg1 arg2"),
)
assert.Equal(
t,
replCommand{
args: []string{"arg1", "arg2", "arg3"},
flags: map[string]string{
"flag1": "value1",
"flag2": "value2",
},
},
parseReplInputs("arg1 arg2 --flag1=value1 arg3 --flag2=value2"),
)
}

func TestGetBoolValue(t *testing.T) {
command := replCommand{
flags: map[string]string{
"key1": "",
"key2": "true",
"key3": "false",
},
}
assert.True(t, command.getBoolValue("key1"))
assert.True(t, command.getBoolValue("key2"))
assert.False(t, command.getBoolValue("key3"))
assert.False(t, command.getBoolValue("non-existent-key"))
}

func TestCheckArgs(t *testing.T) {
command := replCommand{
args: []string{
"arg1",
"arg2",
},
flags: map[string]string{
"key1": "value1",
},
}
assert.NoError(t, command.checkArgs(2, 2, map[string]struct{}{"key1": {}}))
assert.NoError(t, command.checkArgs(2, 3, map[string]struct{}{"key1": {}}))
assert.NoError(t, command.checkArgs(1, 2, map[string]struct{}{"key1": {}}))
assert.NoError(t, command.checkArgs(1, 2, map[string]struct{}{"key1": {}, "key2": {}}))
assert.Error(t, command.checkArgs(3, 3, map[string]struct{}{"key1": {}}))
assert.Error(t, command.checkArgs(3, 5, map[string]struct{}{"key1": {}}))
assert.Error(t, command.checkArgs(2, 2, map[string]struct{}{"key2": {}}))
assert.Error(t, command.checkArgs(2, 2, nil))
}
Loading

0 comments on commit d7f7c32

Please sign in to comment.