Skip to content

Commit 8a39829

Browse files
authored
Adds explicit search command for better UX (#2)
1 parent c390a69 commit 8a39829

20 files changed

+1264
-492
lines changed

cmd/api.go

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import (
1717
"golang.org/x/term"
1818
)
1919

20-
type apiOptions struct {
20+
type APIOptions struct {
2121
method string
2222
requestBody string
2323
inputFile string
@@ -26,12 +26,12 @@ type apiOptions struct {
2626
noColor bool
2727
}
2828

29-
func newApiCmd() *cobra.Command {
30-
opts := apiOptions{}
29+
func NewCmdAPI() *cobra.Command {
30+
opts := APIOptions{}
3131

3232
cmd := &cobra.Command{
33-
Use: "api <endpoint>",
34-
Short: "Make authenticated requests to the Glean API",
33+
Use: "api",
34+
Short: "Make an authenticated HTTP request to the Glean API",
3535
Long: heredoc.Doc(`
3636
Makes an authenticated HTTP request to the Glean API and prints the response.
3737
@@ -155,10 +155,6 @@ func newApiCmd() *cobra.Command {
155155
return cmd
156156
}
157157

158-
func init() {
159-
rootCmd.AddCommand(newApiCmd())
160-
}
161-
162158
func previewRequest(req *http.Request, noColor bool) error {
163159
cfg, err := config.LoadConfig()
164160
if err != nil {
@@ -200,15 +196,13 @@ func previewRequest(req *http.Request, noColor bool) error {
200196
return nil
201197
}
202198

203-
// maskToken masks most of the token characters for display
204199
func maskToken(token string) string {
205200
if len(token) <= 8 {
206201
return strings.Repeat("*", len(token))
207202
}
208203
return token[:4] + strings.Repeat("*", len(token)-8) + token[len(token)-4:]
209204
}
210205

211-
// isatty returns true if the given file descriptor is a terminal
212206
func isatty(fd uintptr) bool {
213207
return term.IsTerminal(int(fd))
214208
}

cmd/api_test.go

Lines changed: 10 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -2,73 +2,42 @@ package cmd
22

33
import (
44
"bytes"
5-
"fmt"
65
"testing"
76

8-
"github.com/spf13/cobra"
97
"github.com/stretchr/testify/assert"
108
)
119

1210
func TestApiCmd(t *testing.T) {
1311
tests := []struct {
14-
name string
15-
wantOutput string
16-
args []string
17-
wantErr bool
12+
name string
13+
args []string
14+
wantErr bool
1815
}{
1916
{
20-
name: "default GET method",
21-
args: []string{"/search"},
22-
wantOutput: "Invoking Glean API with method=GET, endpoint=/search",
17+
name: "no endpoint provided",
18+
args: []string{},
19+
wantErr: true,
2320
},
2421
{
25-
name: "POST method",
26-
args: []string{"--method", "POST", "/users"},
27-
wantOutput: "Invoking Glean API with method=POST, endpoint=/users",
28-
},
29-
{
30-
name: "custom method with -X flag",
31-
args: []string{"-X", "PUT", "/update"},
32-
wantOutput: "Invoking Glean API with method=PUT, endpoint=/update",
33-
},
34-
{
35-
name: "no endpoint provided",
36-
args: []string{},
37-
wantOutput: "Invoking Glean API with method=GET, endpoint=",
22+
name: "help flag",
23+
args: []string{"--help"},
24+
wantErr: false,
3825
},
3926
}
4027

4128
for _, tt := range tests {
4229
t.Run(tt.name, func(t *testing.T) {
43-
// Create a fresh command for each test
44-
cmd := &cobra.Command{
45-
Use: "api",
46-
Short: "Make calls to the Glean API",
47-
RunE: func(cmd *cobra.Command, args []string) error {
48-
method, _ := cmd.Flags().GetString("method")
49-
endpoint := ""
50-
if len(args) > 0 {
51-
endpoint = args[0]
52-
}
53-
fmt.Fprintf(cmd.OutOrStdout(), "Invoking Glean API with method=%s, endpoint=%s\n", method, endpoint)
54-
return nil
55-
},
56-
}
57-
cmd.Flags().StringP("method", "X", "GET", "HTTP method to use (GET, POST, etc.)")
58-
5930
b := bytes.NewBufferString("")
31+
cmd := NewCmdAPI()
6032
cmd.SetOut(b)
6133
cmd.SetErr(b)
6234
cmd.SetArgs(tt.args)
6335

6436
err := cmd.Execute()
65-
6637
if tt.wantErr {
6738
assert.Error(t, err)
6839
} else {
6940
assert.NoError(t, err)
70-
output := b.String()
71-
assert.Contains(t, output, tt.wantOutput)
7241
}
7342
})
7443
}

cmd/config.go

Lines changed: 69 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -9,78 +9,90 @@ import (
99
"github.com/spf13/cobra"
1010
)
1111

12-
var (
12+
const notSetValue = "[not set]"
13+
14+
type ConfigOptions struct {
1315
host string
1416
token string
1517
email string
1618
clear bool
1719
show bool
18-
)
19-
20-
const notSetValue = "[not set]"
20+
}
2121

22-
var configCmd = &cobra.Command{
23-
Use: "config",
24-
Short: "Configure Glean CLI credentials",
25-
Long: heredoc.Doc(`
26-
Configure credentials for the Glean CLI.
27-
28-
Examples:
29-
# Set Glean host (either format works)
30-
glean config --host linkedin
31-
glean config --host linkedin-be.glean.com
32-
33-
# Set Glean API token
34-
glean config --token your-token
35-
36-
# Set Glean user email
37-
glean config --email user@company.com
38-
39-
# Show current configuration
40-
glean config --show
41-
42-
# Clear all stored credentials
43-
glean config --clear
44-
`),
45-
RunE: func(cmd *cobra.Command, args []string) error {
46-
if show {
47-
cfg, err := config.LoadConfig()
48-
if err != nil {
49-
return fmt.Errorf("failed to access keyring: %w", err)
22+
func NewCmdConfig() *cobra.Command {
23+
opts := ConfigOptions{}
24+
25+
cmd := &cobra.Command{
26+
Use: "config",
27+
Short: "Manage Glean CLI configuration",
28+
Long: heredoc.Doc(`
29+
Configure credentials for the Glean CLI.
30+
31+
Examples:
32+
# Set Glean host (either format works)
33+
glean config --host linkedin
34+
glean config --host linkedin-be.glean.com
35+
36+
# Set Glean API token
37+
glean config --token your-token
38+
39+
# Set Glean user email
40+
glean config --email user@company.com
41+
42+
# Show current configuration
43+
glean config --show
44+
45+
# Clear all stored credentials
46+
glean config --clear
47+
`),
48+
RunE: func(cmd *cobra.Command, args []string) error {
49+
if opts.show {
50+
cfg, err := config.LoadConfig()
51+
if err != nil {
52+
return fmt.Errorf("failed to access keyring: %w", err)
53+
}
54+
55+
fmt.Println("Current configuration:")
56+
fmt.Printf(" %-10s %s\n", "Host:", valueOrNotSet(cfg.GleanHost))
57+
fmt.Printf(" %-10s %s\n", "Email:", valueOrNotSet(cfg.GleanEmail))
58+
59+
// Mask token if present
60+
tokenDisplay := notSetValue
61+
if cfg.GleanToken != "" {
62+
tokenDisplay = cfg.GleanToken[0:4] + strings.Repeat("*", len(cfg.GleanToken)-4)
63+
}
64+
fmt.Printf(" %-10s %s\n", "Token:", tokenDisplay)
65+
return nil
5066
}
5167

52-
fmt.Println("Current configuration:")
53-
fmt.Printf(" %-10s %s\n", "Host:", valueOrNotSet(cfg.GleanHost))
54-
fmt.Printf(" %-10s %s\n", "Email:", valueOrNotSet(cfg.GleanEmail))
68+
if opts.clear {
69+
if err := config.ClearConfig(); err != nil {
70+
return fmt.Errorf("failed to clear configuration: %w", err)
71+
}
72+
fmt.Println("Configuration cleared successfully")
73+
return nil
74+
}
5575

56-
// Mask token if present
57-
tokenDisplay := notSetValue
58-
if cfg.GleanToken != "" {
59-
tokenDisplay = cfg.GleanToken[0:4] + strings.Repeat("*", len(cfg.GleanToken)-4)
76+
if opts.host == "" && opts.token == "" && opts.email == "" {
77+
return fmt.Errorf("no configuration provided. Use --host, --token, or --email to set configuration")
6078
}
61-
fmt.Printf(" %-10s %s\n", "Token:", tokenDisplay)
62-
return nil
63-
}
6479

65-
if clear {
66-
if err := config.ClearConfig(); err != nil {
67-
return fmt.Errorf("failed to clear configuration: %w", err)
80+
if err := config.SaveConfig(opts.host, opts.token, opts.email); err != nil {
81+
return fmt.Errorf("failed to save configuration: %w", err)
6882
}
69-
fmt.Println("Configuration cleared successfully")
70-
return nil
71-
}
7283

73-
if host == "" && token == "" && email == "" {
74-
return fmt.Errorf("no configuration provided. Use --host, --token, or --email to set configuration")
75-
}
84+
fmt.Println("Configuration saved successfully")
85+
return nil
86+
},
87+
}
7688

77-
if err := config.SaveConfig(host, token, email); err != nil {
78-
return fmt.Errorf("failed to save configuration: %w", err)
79-
}
89+
cmd.Flags().StringVar(&opts.host, "host", "", "Glean instance name or full hostname (e.g., 'linkedin' or 'linkedin-be.glean.com')")
90+
cmd.Flags().StringVar(&opts.token, "token", "", "Glean API token")
91+
cmd.Flags().StringVar(&opts.email, "email", "", "Email address for API requests")
92+
cmd.Flags().BoolVar(&opts.clear, "clear", false, "Clear all stored credentials")
93+
cmd.Flags().BoolVar(&opts.show, "show", false, "Show current configuration")
8094

81-
fmt.Println("Configuration saved successfully")
82-
return nil
83-
},
95+
return cmd
8496
}
8597

8698
func valueOrNotSet(value string) string {
@@ -89,13 +101,3 @@ func valueOrNotSet(value string) string {
89101
}
90102
return value
91103
}
92-
93-
func init() {
94-
rootCmd.AddCommand(configCmd)
95-
96-
configCmd.Flags().StringVar(&host, "host", "", "Glean instance name or full hostname (e.g., 'linkedin' or 'linkedin-be.glean.com')")
97-
configCmd.Flags().StringVar(&token, "token", "", "Glean API token")
98-
configCmd.Flags().StringVar(&email, "email", "", "Email address for API requests")
99-
configCmd.Flags().BoolVar(&clear, "clear", false, "Clear all stored credentials")
100-
configCmd.Flags().BoolVar(&show, "show", false, "Show current configuration")
101-
}

0 commit comments

Comments
 (0)