Repository: nats-io/nsc
Pull request #85 (2019-05-13 21:52:20)

add support for a list flag
some boiler plate utility to implement flags like '--tag' in an interactive mode.


Patch:
diff --git a/cli/prompts.go b/cli/prompts.go
index 569cd6b..60656df 100644
--- a/cli/prompts.go
+++ b/cli/prompts.go
@@ -8,10 +8,15 @@ import (
 	"os"
 )
 
+type Logger func(args ...interface{})
+
 type Validator func(string) error
 
 var cli PromptLib
 
+// set a Logger during a test (cli.LogFn = t.Log) to debug interactive prompts
+var LogFn Logger
+
 var output io.Writer = os.Stdout
 
 type PromptLib interface {
@@ -24,6 +29,7 @@ type PromptLib interface {
 
 func init() {
 	ResetPromptLib()
+	LogFn = nil
 }
 
 func SetPromptLib(p PromptLib) {
@@ -32,6 +38,7 @@ func SetPromptLib(p PromptLib) {
 
 func ResetPromptLib() {
 	SetPromptLib(&SurveyUI{})
+	LogFn = nil
 }
 
 func SetOutput(out io.Writer) {
diff --git a/cmd/editaccount.go b/cmd/editaccount.go
index 70ca85e..1153a6a 100644
--- a/cmd/editaccount.go
+++ b/cmd/editaccount.go
@@ -91,7 +91,7 @@ type EditAccountParams struct {
 	claim         *jwt.AccountClaims
 	token         string
 	conns         NumberParams
-	leafConns	  NumberParams
+	leafConns     NumberParams
 	exports       NumberParams
 	exportsWc     bool
 	imports       NumberParams
diff --git a/cmd/listeditor.go b/cmd/listeditor.go
new file mode 100644
index 0000000..846edf2
--- /dev/null
+++ b/cmd/listeditor.go
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2018-2019 The NATS Authors
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cmd
+
+import (
+	"fmt"
+
+	"github.com/nats-io/nsc/cli"
+)
+
+type ListEditorParam struct {
+	PromptMessage string
+	AddMessage    string
+	FlagName      string
+	Values        []string
+	ValidatorFn   cli.Validator
+}
+
+func (e *ListEditorParam) Valid() error {
+	if e.ValidatorFn == nil {
+		return nil
+	}
+	for _, v := range e.Values {
+		if err := e.ValidatorFn(v); err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+func (e *ListEditorParam) GetValues() []string {
+	return e.Values
+}
+
+func (e *ListEditorParam) Edit() error {
+	if e.PromptMessage == "" {
+		e.PromptMessage = fmt.Sprintf("edit %s", e.FlagName)
+	}
+	if e.AddMessage == "" {
+		e.AddMessage = fmt.Sprintf("add %s", e.FlagName)
+	}
+	for i, v := range e.Values {
+		sv, err := cli.Prompt(e.PromptMessage, v, true, e.ValidatorFn)
+		if err != nil {
+			return err
+		}
+		e.Values[i] = sv
+	}
+	for {
+		ok, err := cli.PromptYN(e.AddMessage)
+		if err != nil {
+			return err
+		}
+		if !ok {
+			break
+		}
+		sv, err := cli.Prompt(e.PromptMessage, "", true, e.ValidatorFn)
+		if err != nil {
+			return err
+		}
+		e.Values = append(e.Values, sv)
+	}
+	return nil
+}


Test patch:
diff --git a/cli/testprompts.go b/cli/testprompts.go
index 7eca138..7c44367 100644
--- a/cli/testprompts.go
+++ b/cli/testprompts.go
@@ -15,6 +15,8 @@
 
 package cli
 
+import "fmt"
+
 type TestPrompts struct {
 	count  int
 	inputs []interface{}
@@ -27,7 +29,15 @@ func NewTestPrompts(inputs []interface{}) PromptLib {
 	}
 }
 
+func (t *TestPrompts) logInputs(kind string, label string, value interface{}) {
+	if LogFn == nil {
+		return
+	}
+	LogFn(fmt.Sprintf("[%s %d] %s = %v\n", kind, t.count, label, value))
+}
+
 func (t *TestPrompts) Prompt(label string, value string, edit bool, validator Validator) (string, error) {
+	t.logInputs("prompt", label, t.inputs[t.count])
 	val := t.inputs[t.count].(string)
 	t.count = t.count + 1
 
@@ -41,24 +51,28 @@ func (t *TestPrompts) Prompt(label string, value string, edit bool, validator Va
 }
 
 func (t *TestPrompts) PromptYN(m string, defaultValue bool) (bool, error) {
+	t.logInputs("yn", m, t.inputs[t.count])
 	val := t.inputs[t.count].(bool)
 	t.count = t.count + 1
 	return val, nil
 }
 
 func (t *TestPrompts) PromptSecret(m string) (string, error) {
+	t.logInputs("secret", m, t.inputs[t.count])
 	val := t.inputs[t.count].(string)
 	t.count = t.count + 1
 	return val, nil
 }
 
 func (t *TestPrompts) PromptChoices(m string, value string, choices []string) (int, error) {
+	t.logInputs("choices", m, t.inputs[t.count])
 	val := t.inputs[t.count].(int)
 	t.count = t.count + 1
 	return val, nil
 }
 
 func (t *TestPrompts) PromptMultipleChoices(m string, choices []string) ([]int, error) {
+	t.logInputs("multiple-choice", m, t.inputs[t.count])
 	val := t.inputs[t.count].([]int)
 	t.count = t.count + 1
 	return val, nil
diff --git a/cmd/deleteimport_test.go b/cmd/deleteimport_test.go
index bc55e51..aa7112c 100644
--- a/cmd/deleteimport_test.go
+++ b/cmd/deleteimport_test.go
@@ -119,4 +119,4 @@ func Test_DeleteImportInteractiveNoStreams(t *testing.T) {
 	HoistRootFlags(cmd)
 	_, _, err := ExecuteInteractiveCmd(cmd, input)
 	require.Contains(t, err.Error(), "no stream imports defined")
-}
\ No newline at end of file
+}
diff --git a/cmd/listeditor_test.go b/cmd/listeditor_test.go
new file mode 100644
index 0000000..bdcf87f
--- /dev/null
+++ b/cmd/listeditor_test.go
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2018-2019 The NATS Authors
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cmd
+
+import (
+	"testing"
+
+	"github.com/spf13/cobra"
+	"github.com/stretchr/testify/require"
+)
+
+func listEditorCmd(params *ListEditorParam) *cobra.Command {
+	cmd := &cobra.Command{
+		Use: "test",
+		RunE: func(cmd *cobra.Command, args []string) error {
+			if InteractiveFlag {
+				return params.Edit()
+			}
+			return params.Valid()
+		},
+	}
+	cmd.Flags().StringSliceVarP(&params.Values, params.FlagName, "", nil, "")
+	return cmd
+}
+
+func TestListEditorParams(t *testing.T) {
+	var p ListEditorParam
+	p.FlagName = "test"
+	validatorCalled := false
+	p.ValidatorFn = func(string) error {
+		validatorCalled = true
+		return nil
+	}
+	cmd := listEditorCmd(&p)
+	_, _, err := ExecuteCmd(cmd, "--test", "a", "--test", "b")
+	require.NoError(t, err)
+	require.True(t, validatorCalled)
+	require.Len(t, p.Values, 2)
+	require.EqualValues(t, p.Values, []string{"a", "b"})
+}
+
+func TestListEditorParams_Interactive(t *testing.T) {
+	var p ListEditorParam
+	p.FlagName = "test"
+	validatorCalled := false
+	p.ValidatorFn = func(string) error {
+		validatorCalled = true
+		return nil
+	}
+	cmd := listEditorCmd(&p)
+	_, _, err := ExecuteInteractiveCmd(cmd, []interface{}{true, "x", true, "y", false})
+	require.NoError(t, err)
+	require.True(t, validatorCalled)
+	require.Len(t, p.Values, 2)
+	require.EqualValues(t, p.Values, []string{"x", "y"})
+}
+
+func TestListEditorParams_InteractiveEdit(t *testing.T) {
+	p := &ListEditorParam{}
+	p.FlagName = "test"
+	validatorCalled := false
+	p.ValidatorFn = func(string) error {
+		validatorCalled = true
+		return nil
+	}
+	cmd := listEditorCmd(p)
+	_, _, err := ExecuteInteractiveCmd(cmd, []interface{}{"aa", "bb", true, "x", true, "y", false}, "--test", "a", "--test", "b")
+	require.NoError(t, err)
+	require.True(t, validatorCalled)
+	require.Len(t, p.Values, 4)
+	require.EqualValues(t, p.Values, []string{"aa", "bb", "x", "y"})
+	require.EqualValues(t, p.GetValues(), []string{"aa", "bb", "x", "y"})
+
+	t.Log()
+}
diff --git a/cmd/util_test.go b/cmd/util_test.go
index 116905d..972578f 100644
--- a/cmd/util_test.go
+++ b/cmd/util_test.go
@@ -245,8 +245,6 @@ func (ts *TestStore) AddImport(t *testing.T, srcAccount string, subject string,
 	require.NoError(t, err)
 }
 
-
-
 func (ts *TestStore) GenerateActivation(t *testing.T, srcAccount string, subject string, targetAccount string) string {
 	tpub, err := ts.KeyStore.GetAccountPublicKey(targetAccount)
 	require.NoError(t, err)