Skip to content

Commit

Permalink
Update/refactor utility functions
Browse files Browse the repository at this point in the history
- Replace 'containsString' function with slices.Contains.
- Move 'confirm' function to util package.
  • Loading branch information
tjmoore4 committed Nov 3, 2023
1 parent 19c3fb0 commit 62bdf7b
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 69 deletions.
47 changes: 2 additions & 45 deletions internal/cmd/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,16 @@
package cmd

import (
"bufio"
"context"
"fmt"
"io"
"os"

"github.com/spf13/cobra"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

"github.com/crunchydata/postgres-operator-client/internal"
"github.com/crunchydata/postgres-operator-client/internal/apis/postgres-operator.crunchydata.com/v1beta1"
"github.com/crunchydata/postgres-operator-client/internal/util"
)

// newDeleteCommand returns the delete subcommand of the PGO plugin.
Expand Down Expand Up @@ -80,7 +79,7 @@ postgresclusters/hippo deleted`)
for i := 0; confirmed == nil && i < 10; i++ {
// retry 10 times or until a confirmation is given or denied,
// whichever comes first
confirmed = confirm(os.Stdin, os.Stdout)
confirmed = util.Confirm(os.Stdin, os.Stdout)
}

if confirmed == nil || !*confirmed {
Expand Down Expand Up @@ -111,45 +110,3 @@ postgresclusters/hippo deleted`)

return cmd
}

// confirm uses a Scanner to parse user input. A user must type in "yes" or "no"
// and then press enter. It has fuzzy matching, so "y", "Y", "yes", "YES",
// and "Yes" all count as confirmations and return 'true'. Similarly, "n", "N",
// "no", "No", "NO" all deny confirmation and return 'false'. If the input is not
// recognized, nil is returned.
func confirm(reader io.Reader, writer io.Writer) *bool {
var response string
var boolVar bool

scanner := bufio.NewScanner(reader)
if scanner.Scan() {
response = scanner.Text()
}

if scanner.Err() != nil || response == "" {
fmt.Fprint(writer, "Please type yes or no and then press enter: ")
return nil
}

yesResponses := []string{"y", "Y", "yes", "Yes", "YES"}
noResponses := []string{"n", "N", "no", "No", "NO"}
if containsString(yesResponses, response) {
boolVar = true
return &boolVar
} else if containsString(noResponses, response) {
return &boolVar
} else {
fmt.Fprint(writer, "Please type yes or no and then press enter: ")
return nil
}
}

// containsString returns true if slice contains element
func containsString(slice []string, element string) bool {
for _, elem := range slice {
if elem == element {
return true
}
}
return false
}
3 changes: 2 additions & 1 deletion internal/cmd/restore.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (

"github.com/crunchydata/postgres-operator-client/internal"
"github.com/crunchydata/postgres-operator-client/internal/apis/postgres-operator.crunchydata.com/v1beta1"
"github.com/crunchydata/postgres-operator-client/internal/util"
)

func newRestoreCommand(config *internal.Config) *cobra.Command {
Expand Down Expand Up @@ -225,7 +226,7 @@ func (config pgBackRestRestore) Run(ctx context.Context) error {

func (config pgBackRestRestore) confirm(attempts int) *bool {
for i := 0; i < attempts; i++ {
if confirmed := confirm(config.In, config.Out); confirmed != nil {
if confirmed := util.Confirm(config.In, config.Out); confirmed != nil {
return confirmed
}
}
Expand Down
5 changes: 3 additions & 2 deletions internal/cmd/show.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
v1 "k8s.io/client-go/kubernetes/typed/core/v1"
"k8s.io/utils/strings/slices"
"sigs.k8s.io/yaml"

"github.com/crunchydata/postgres-operator-client/internal"
Expand Down Expand Up @@ -353,7 +354,7 @@ func showUser(config *internal.Config, args []string, showSensitive bool) (strin
for i := 0; confirmed == nil && i < 10; i++ {
// retry 10 times or until a confirmation is given or denied,
// whichever comes first
confirmed = confirm(os.Stdin, os.Stdout)
confirmed = util.Confirm(os.Stdin, os.Stdout)
}

if confirmed == nil || !*confirmed {
Expand Down Expand Up @@ -420,7 +421,7 @@ func userData(fields []string, list *corev1.SecretList) (string, error) {
if err != nil {
return output, err
}
if containsString(fields, k) {
if slices.Contains(fields, k) {
output += fmt.Sprintf(" %s: %s\n", strings.ToUpper(k), string(d))
}
}
Expand Down
55 changes: 55 additions & 0 deletions internal/util/util.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// Copyright 2021 - 2023 Crunchy Data Solutions, Inc.
//
// 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 util

import (
"bufio"
"fmt"
"io"

"k8s.io/utils/strings/slices"
)

// Confirm uses a Scanner to parse user input. A user must type in "yes" or "no"
// and then press enter. It has fuzzy matching, so "y", "Y", "yes", "YES",
// and "Yes" all count as confirmations and return 'true'. Similarly, "n", "N",
// "no", "No", "NO" all deny confirmation and return 'false'. If the input is not
// recognized, nil is returned.
func Confirm(reader io.Reader, writer io.Writer) *bool {
var response string
var boolVar bool

scanner := bufio.NewScanner(reader)
if scanner.Scan() {
response = scanner.Text()
}

if scanner.Err() != nil || response == "" {
fmt.Fprint(writer, "Please type yes or no and then press enter: ")
return nil
}

yesResponses := []string{"y", "Y", "yes", "Yes", "YES"}
noResponses := []string{"n", "N", "no", "No", "NO"}
if slices.Contains(yesResponses, response) {
boolVar = true
return &boolVar
} else if slices.Contains(noResponses, response) {
return &boolVar
} else {
fmt.Fprint(writer, "Please type yes or no and then press enter: ")
return nil
}
}
23 changes: 2 additions & 21 deletions internal/cmd/delete_test.go → internal/util/util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

package cmd
package util

import (
"bytes"
Expand Down Expand Up @@ -50,7 +50,7 @@ func TestConfirmDelete(t *testing.T) {
t.Run("input is "+tc.input, func(t *testing.T) {
var reader io.Reader = strings.NewReader(tc.input)
var writer bytes.Buffer
confirmed := confirm(reader, &writer)
confirmed := Confirm(reader, &writer)
if tc.invalidResponse {
assert.Assert(t, confirmed == nil)
response, err := writer.ReadString(':')
Expand All @@ -64,22 +64,3 @@ func TestConfirmDelete(t *testing.T) {
})
}
}

func TestContainsString(t *testing.T) {
testsCases := []struct {
desc string
slice []string
element string
found bool
}{
{"found", []string{"a", "b", "c"}, "a", true},
{"not found", []string{"a", "b", "c"}, "x", false},
{"not found substring", []string{"ab", "bc", "cd"}, "b", false},
}

for _, tc := range testsCases {
t.Run(tc.desc, func(t *testing.T) {
assert.Equal(t, containsString(tc.slice, tc.element), tc.found)
})
}
}

0 comments on commit 62bdf7b

Please sign in to comment.