Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion internal/pkg/cli/command/auth/configure.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ var (
When you configure a service account, the CLI automatically targets the organization
associated with that account, and prompts you to select a project if multiple exist.

An API overrides any explicitly targeted organization and project, instead targeting
An API key overrides any explicitly targeted organization and project, instead targeting
the organization and project associated with the API key itself. API keys do not grant
Admin API access.

Expand Down
12 changes: 10 additions & 2 deletions internal/pkg/cli/command/config/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,17 @@ func NewConfigCmd() *cobra.Command {
Long: configHelp,
}

cmd.AddCommand(NewSetColorCmd())
cmd.AddCommand(NewSetApiKeyCmd())
// Primary commands
cmd.AddCommand(NewGetCmd())
cmd.AddCommand(NewSetCmd())
cmd.AddCommand(NewUnsetCmd())
cmd.AddCommand(NewListCmd())
cmd.AddCommand(NewDescribeCmd())

// Deprecated aliases kept for backwards compatibility
cmd.AddCommand(NewGetApiKeyCmd())
cmd.AddCommand(NewSetApiKeyCmd())
cmd.AddCommand(NewSetColorCmd())
cmd.AddCommand(NewSetEnvCmd())

return cmd
Expand Down
58 changes: 58 additions & 0 deletions internal/pkg/cli/command/config/config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package config

import "context"

// mockConfigService implements ConfigService for unit tests.
// Each field controls what the corresponding method returns.
// The last* fields record the arguments of the most recent call.
type mockConfigService struct {
// Get
getValue string
getSensitive bool
getErr error
lastGetKey string

// Set
setLines []string
setErr error
lastSetKey string
lastSetValue string

// Unset
unsetLines []string
unsetErr error
lastUnsetKey string

// List
listResult []ConfigEntry

// Describe
describeResult ConfigDescription
describeErr error
lastDescribeKey string
}

func (m *mockConfigService) Get(key string) (string, bool, error) {
m.lastGetKey = key
return m.getValue, m.getSensitive, m.getErr
}

func (m *mockConfigService) Set(ctx context.Context, key, value string) ([]string, error) {
m.lastSetKey = key
m.lastSetValue = value
return m.setLines, m.setErr
}

func (m *mockConfigService) Unset(ctx context.Context, key string) ([]string, error) {
m.lastUnsetKey = key
return m.unsetLines, m.unsetErr
}

func (m *mockConfigService) List() []ConfigEntry {
return m.listResult
}

func (m *mockConfigService) Describe(key string) (ConfigDescription, error) {
m.lastDescribeKey = key
return m.describeResult, m.describeErr
}
98 changes: 98 additions & 0 deletions internal/pkg/cli/command/config/describe.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package config

import (
"fmt"
"os"
"strings"

"github.com/pinecone-io/cli/internal/pkg/utils/exit"
"github.com/pinecone-io/cli/internal/pkg/utils/help"
"github.com/pinecone-io/cli/internal/pkg/utils/msg"
"github.com/pinecone-io/cli/internal/pkg/utils/presenters"
"github.com/pinecone-io/cli/internal/pkg/utils/text"
"github.com/spf13/cobra"
)

type DescribeCmdOptions struct {
reveal bool
json bool
}

func NewDescribeCmd() *cobra.Command {
options := DescribeCmdOptions{}

cmd := &cobra.Command{
Use: "describe <key>",
Short: "Show detailed information about a configuration setting",
Example: help.Examples(`
pc config describe api-key
pc config describe environment
pc config describe color --json
`),
Args: cobra.ExactArgs(1),
ValidArgs: visibleKeys(),
Run: func(cmd *cobra.Command, args []string) {
svc := newDefaultConfigService()
if err := runDescribeCmd(svc, args[0], options); err != nil {
msg.FailJSON(options.json, "%s", err)
exit.ErrorMsg(err.Error())
}
},
}

cmd.Flags().BoolVar(&options.reveal, "reveal", false, "Reveal the full value for sensitive settings like api-key")
cmd.Flags().BoolVarP(&options.json, "json", "j", false, "Output as JSON")

return cmd
}

func runDescribeCmd(svc ConfigService, keyName string, opts DescribeCmdOptions) error {
// --json output for the describe command
type describeOutput struct {
Key string `json:"key"`
Value string `json:"value"`
Description string `json:"description"`
LongDescription string `json:"long_description,omitempty"`
Sensitive bool `json:"sensitive"`
ValidValues []string `json:"valid_values,omitempty"`
}

desc, err := svc.Describe(keyName)
if err != nil {
return err
}

value := desc.Value
if desc.Sensitive && !opts.reveal {
value = presenters.MaskHeadTail(value, 4, 4)
}

if opts.json {
fmt.Fprintln(os.Stdout, text.IndentJSON(describeOutput{
Key: desc.Key,
Value: value,
Description: desc.Description,
LongDescription: desc.LongDescription,
Sensitive: desc.Sensitive,
ValidValues: desc.ValidValues,
}))
return nil
}

w := presenters.NewTabWriter()
fmt.Fprintf(w, "KEY\t%s\n", desc.Key)
fmt.Fprintf(w, "VALUE\t%s\n", displayValue(value))
fmt.Fprintf(w, "SENSITIVE\t%s\n", text.BoolToString(desc.Sensitive))
if len(desc.ValidValues) > 0 {
fmt.Fprintf(w, "VALID VALUES\t%s\n", strings.Join(desc.ValidValues, ", "))
}
fmt.Fprintf(w, "DESCRIPTION\t%s\n", desc.Description)
w.Flush()

if desc.LongDescription != "" {
fmt.Fprintln(os.Stdout)
fmt.Fprintln(os.Stdout, desc.LongDescription)
}

return nil
}
93 changes: 93 additions & 0 deletions internal/pkg/cli/command/config/describe_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package config

import (
"errors"
"testing"

"github.com/pinecone-io/cli/internal/pkg/cli/testutils"
"github.com/stretchr/testify/assert"
)

func Test_runDescribeCmd_ReturnsErrorOnUnknownKey(t *testing.T) {
svc := &mockConfigService{describeErr: errors.New("unknown config key")}

err := runDescribeCmd(svc, "bad-key", DescribeCmdOptions{})

assert.Error(t, err)
assert.Equal(t, "bad-key", svc.lastDescribeKey)
}

func Test_runDescribeCmd_TabularOutput(t *testing.T) {
svc := &mockConfigService{
describeResult: ConfigDescription{
Key: "environment",
Value: "production",
Description: "Pinecone environment",
Sensitive: false,
ValidValues: []string{"production", "staging"},
},
}

out := testutils.CaptureStdout(t, func() {
err := runDescribeCmd(svc, "environment", DescribeCmdOptions{})
assert.NoError(t, err)
})

assert.Contains(t, out, "environment")
assert.Contains(t, out, "production")
}

func Test_runDescribeCmd_JSONOutput(t *testing.T) {
svc := &mockConfigService{
describeResult: ConfigDescription{
Key: "environment",
Value: "production",
Description: "Pinecone environment",
Sensitive: false,
ValidValues: []string{"production", "staging"},
},
}

out := testutils.CaptureStdout(t, func() {
err := runDescribeCmd(svc, "environment", DescribeCmdOptions{json: true})
assert.NoError(t, err)
})

assert.Contains(t, out, `"environment"`)
assert.Contains(t, out, `"production"`)
assert.Contains(t, out, `"valid_values"`)
}

func Test_runDescribeCmd_MasksSensitiveKeyInJSON(t *testing.T) {
svc := &mockConfigService{
describeResult: ConfigDescription{
Key: "api-key",
Value: "supersecretvalue",
Sensitive: true,
},
}

out := testutils.CaptureStdout(t, func() {
err := runDescribeCmd(svc, "api-key", DescribeCmdOptions{json: true, reveal: false})
assert.NoError(t, err)
})

assert.NotContains(t, out, "supersecretvalue")
}

func Test_runDescribeCmd_RevealsSensitiveKeyInJSON(t *testing.T) {
svc := &mockConfigService{
describeResult: ConfigDescription{
Key: "api-key",
Value: "supersecretvalue",
Sensitive: true,
},
}

out := testutils.CaptureStdout(t, func() {
err := runDescribeCmd(svc, "api-key", DescribeCmdOptions{json: true, reveal: true})
assert.NoError(t, err)
})

assert.Contains(t, out, "supersecretvalue")
}
73 changes: 73 additions & 0 deletions internal/pkg/cli/command/config/get.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package config

import (
"fmt"
"os"

"github.com/pinecone-io/cli/internal/pkg/utils/exit"
"github.com/pinecone-io/cli/internal/pkg/utils/help"
"github.com/pinecone-io/cli/internal/pkg/utils/msg"
"github.com/pinecone-io/cli/internal/pkg/utils/presenters"
"github.com/pinecone-io/cli/internal/pkg/utils/style"
"github.com/pinecone-io/cli/internal/pkg/utils/text"
"github.com/spf13/cobra"
)

type GetCmdOptions struct {
reveal bool
json bool
}

func NewGetCmd() *cobra.Command {
options := GetCmdOptions{}

cmd := &cobra.Command{
Use: "get <key>",
Short: "Get the current value of a configuration setting",
Example: help.Examples(`
pc config get api-key
pc config get api-key --reveal
pc config get environment
pc config get color
`),
Args: cobra.ExactArgs(1),
ValidArgs: visibleKeys(),
Run: func(cmd *cobra.Command, args []string) {
svc := newDefaultConfigService()
if err := runGetCmd(svc, args[0], options); err != nil {
msg.FailJSON(options.json, "%s", err)
exit.ErrorMsg(err.Error())
Comment thread
cursor[bot] marked this conversation as resolved.
}
},
}

cmd.Flags().BoolVar(&options.reveal, "reveal", false, "Reveal the full value for sensitive settings like api-key")
cmd.Flags().BoolVarP(&options.json, "json", "j", false, "Output as JSON")

return cmd
}

func runGetCmd(svc ConfigService, keyName string, opts GetCmdOptions) error {
// --json output for the get command
type getOutput struct {
Key string `json:"key"`
Value string `json:"value"`
}

value, sensitive, err := svc.Get(keyName)
if err != nil {
return err
}

if sensitive && !opts.reveal {
value = presenters.MaskHeadTail(value, 4, 4)
}

if opts.json {
fmt.Fprintln(os.Stdout, text.IndentJSON(getOutput{Key: keyName, Value: value}))
return nil
}

msg.InfoMsg("%s: %s", style.Emphasis(keyName), displayValue(value))
return nil
}
6 changes: 4 additions & 2 deletions internal/pkg/cli/command/config/get_api_key.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@ func NewGetApiKeyCmd() *cobra.Command {
options := GetAPIKeyCmdOptions{}

cmd := &cobra.Command{
Use: "get-api-key",
Short: "Get the current default API key configured for the Pinecone CLI",
Use: "get-api-key",
Short: "Get the current default API key configured for the Pinecone CLI",
Deprecated: "use 'pc config get api-key' instead",
Hidden: true,
Example: help.Examples(`
pc config get-api-key
`),
Expand Down
Loading
Loading