Skip to content

Commit fd86607

Browse files
authored
Switch out huh for survey with a custom renderer template (#794)
1 parent cebcab0 commit fd86607

File tree

4 files changed

+94
-106
lines changed

4 files changed

+94
-106
lines changed

pkg/assume/assume.go

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -777,21 +777,21 @@ func QueryProfiles(profiles *cfaws.Profiles) (string, error) {
777777
originalSelectTemplate := survey.SelectQuestionTemplate
778778
survey.SelectQuestionTemplate = fmt.Sprintf(`
779779
{{- define "option"}}
780-
{{- if eq .SelectedIndex .CurrentIndex }}{{color .Config.Icons.SelectFocus.Format }}{{ .Config.Icons.SelectFocus.Text }} {{else}}{{color "default"}} {{end}}
781-
{{- .CurrentOpt.Value}}
782-
{{- color "reset"}}
780+
{{- if eq .SelectedIndex .CurrentIndex }}{{color .Config.Icons.SelectFocus.Format }}{{ .Config.Icons.SelectFocus.Text }} {{else}}{{color "default"}} {{end}}
781+
{{- .CurrentOpt.Value}}{{ if ne ($.GetDescription .CurrentOpt) "" }} - {{color "cyan"}}{{ $.GetDescription .CurrentOpt }}{{end}}
782+
{{- color "reset"}}
783783
{{end}}
784784
{{- if .ShowHelp }}{{- color .Config.Icons.Help.Format }}{{ .Config.Icons.Help.Text }} {{ .Help }}{{color "reset"}}{{"\n"}}{{end}}
785785
{{- color .Config.Icons.Question.Format }}{{ .Config.Icons.Question.Text }} {{color "reset"}}
786786
{{- color "default+hb"}}{{ .Message }}{{ .FilterMessage }}{{color "reset"}}
787787
{{- if .ShowAnswer}}{{color "cyan"}} {{.Answer}}{{color "reset"}}{{"\n"}}
788788
{{- else}}
789-
{{- " "}}{{- color "cyan"}}[Use arrows to move, type to filter{{- if and .Help (not .ShowHelp)}}, {{ .Config.HelpInput }} for more help{{end}}]{{color "reset"}}
790-
{{- "\n"}}
791-
%s
792-
{{- range $ix, $option := .PageEntries}}
793-
{{- template "option" $.IterateOption $ix $option}}
794-
{{- end}}
789+
{{- " "}}{{- color "cyan"}}[Use arrows to move, type to filter{{- if and .Help (not .ShowHelp)}}, {{ .Config.HelpInput }} for more help{{end}}]{{color "reset"}}
790+
{{- "\n"}}
791+
%s{{- "\n"}}
792+
{{- range $ix, $option := .PageEntries}}
793+
{{- template "option" $.IterateOption $ix $option}}
794+
{{- end}}
795795
{{- end}}`, promptHeader)
796796

797797
clio.NewLine()

pkg/granted/eks/eks.go

Lines changed: 1 addition & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,13 @@ import (
66

77
"connectrpc.com/connect"
88

9-
"github.com/charmbracelet/huh"
10-
"github.com/charmbracelet/lipgloss"
119
"github.com/common-fate/clio"
1210
"github.com/common-fate/grab"
1311
"github.com/common-fate/granted/pkg/cfcfg"
1412
"github.com/common-fate/granted/pkg/granted/proxy"
1513
"github.com/common-fate/sdk/config"
1614
accessv1alpha1 "github.com/common-fate/sdk/gen/commonfate/access/v1alpha1"
1715
"github.com/common-fate/sdk/service/access"
18-
"github.com/mattn/go-runewidth"
1916

2017
"github.com/urfave/cli/v2"
2118
)
@@ -166,50 +163,5 @@ func promptForClusterAndRole(ctx context.Context, cfg *config.Context) (*accessv
166163
return nil, errors.New("you don't have access to any EKS Clusters")
167164
}
168165

169-
type Column struct {
170-
Title string
171-
Width int
172-
}
173-
cols := []Column{{Title: "Cluster", Width: 40}, {Title: "Role", Width: 40}}
174-
var s = make([]string, 0, len(cols))
175-
for _, col := range cols {
176-
style := lipgloss.NewStyle().Width(col.Width).MaxWidth(col.Width).Inline(true)
177-
renderedCell := style.Render(runewidth.Truncate(col.Title, col.Width, "…"))
178-
s = append(s, lipgloss.NewStyle().Bold(true).Padding(0).Render(renderedCell))
179-
}
180-
header := lipgloss.NewStyle().PaddingLeft(2).Render(lipgloss.JoinHorizontal(lipgloss.Left, s...))
181-
var options []huh.Option[*accessv1alpha1.Entitlement]
182-
183-
for _, entitlement := range entitlements {
184-
style := lipgloss.NewStyle().Width(cols[0].Width).MaxWidth(cols[0].Width).Inline(true)
185-
target := lipgloss.NewStyle().Bold(true).Padding(0).Render(style.Render(runewidth.Truncate(entitlement.Target.Display(), cols[0].Width, "…")))
186-
187-
style = lipgloss.NewStyle().Width(cols[1].Width).MaxWidth(cols[1].Width).Inline(true)
188-
role := lipgloss.NewStyle().Bold(true).Padding(0).Render(style.Render(runewidth.Truncate(entitlement.Role.Display(), cols[1].Width, "…")))
189-
190-
options = append(options, huh.Option[*accessv1alpha1.Entitlement]{
191-
Key: lipgloss.JoinHorizontal(lipgloss.Left, target, role),
192-
Value: entitlement,
193-
})
194-
}
195-
196-
selector := huh.NewSelect[*accessv1alpha1.Entitlement]().
197-
// show the filter dialog when there are 2 or more options
198-
Filtering(len(options) > 1).
199-
Options(options...).
200-
Title("Select a cluster to connect to").
201-
Description(header).WithTheme(huh.ThemeBase())
202-
203-
err = selector.Run()
204-
if err != nil {
205-
return nil, err
206-
}
207-
208-
selectorVal := selector.GetValue()
209-
210-
if selectorVal == nil {
211-
return nil, errors.New("no cluster selected")
212-
}
213-
214-
return selectorVal.(*accessv1alpha1.Entitlement), nil
166+
return proxy.PromptEntitlements(entitlements, "Cluster", "Service Account", "Select a cluster to connect to: ")
215167
}

pkg/granted/proxy/prompt.go

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
package proxy
2+
3+
import (
4+
"fmt"
5+
"strings"
6+
7+
"github.com/AlecAivazis/survey/v2"
8+
"github.com/charmbracelet/lipgloss"
9+
accessv1alpha1 "github.com/common-fate/sdk/gen/commonfate/access/v1alpha1"
10+
"github.com/mattn/go-runewidth"
11+
)
12+
13+
func filterMultiToken(filterValue string, optValue string, optIndex int) bool {
14+
optValue = strings.ToLower(optValue)
15+
filters := strings.Split(strings.ToLower(filterValue), " ")
16+
for _, filter := range filters {
17+
if !strings.Contains(optValue, filter) {
18+
return false
19+
}
20+
}
21+
return true
22+
}
23+
func PromptEntitlements(entitlements []*accessv1alpha1.Entitlement, targetHeader string, roleHeader string, promptMessage string) (*accessv1alpha1.Entitlement, error) {
24+
type Column struct {
25+
Title string
26+
Width int
27+
}
28+
cols := []Column{{Title: targetHeader, Width: 40}, {Title: roleHeader, Width: 40}}
29+
var s = make([]string, 0, len(cols))
30+
for _, col := range cols {
31+
style := lipgloss.NewStyle().Width(col.Width).MaxWidth(col.Width).Inline(true)
32+
renderedCell := style.Render(runewidth.Truncate(col.Title, col.Width, "…"))
33+
s = append(s, lipgloss.NewStyle().Bold(true).Padding(0).Render(renderedCell))
34+
}
35+
header := lipgloss.NewStyle().PaddingLeft(2).Render(lipgloss.JoinHorizontal(lipgloss.Left, s...))
36+
var options []string
37+
optionsMap := make(map[string]*accessv1alpha1.Entitlement)
38+
for i, entitlement := range entitlements {
39+
style := lipgloss.NewStyle().Width(cols[0].Width).MaxWidth(cols[0].Width).Inline(true)
40+
target := lipgloss.NewStyle().Bold(true).Padding(0).Render(style.Render(runewidth.Truncate(entitlement.Target.Display(), cols[0].Width, "…")))
41+
42+
style = lipgloss.NewStyle().Width(cols[1].Width).MaxWidth(cols[1].Width).Inline(true)
43+
role := lipgloss.NewStyle().Bold(true).Padding(0).Render(style.Render(runewidth.Truncate(entitlement.Role.Display(), cols[1].Width, "…")))
44+
45+
option := lipgloss.JoinHorizontal(lipgloss.Left, target, role)
46+
options = append(options, option)
47+
optionsMap[option] = entitlements[i]
48+
}
49+
50+
originalSelectTemplate := survey.SelectQuestionTemplate
51+
survey.SelectQuestionTemplate = fmt.Sprintf(`
52+
{{- define "option"}}
53+
{{- if eq .SelectedIndex .CurrentIndex }}{{color .Config.Icons.SelectFocus.Format }}{{ .Config.Icons.SelectFocus.Text }} {{else}}{{color "default"}} {{end}}
54+
{{- .CurrentOpt.Value}}{{ if ne ($.GetDescription .CurrentOpt) "" }} - {{color "cyan"}}{{ $.GetDescription .CurrentOpt }}{{end}}
55+
{{- color "reset"}}
56+
{{end}}
57+
{{- if .ShowHelp }}{{- color .Config.Icons.Help.Format }}{{ .Config.Icons.Help.Text }} {{ .Help }}{{color "reset"}}{{"\n"}}{{end}}
58+
{{- color .Config.Icons.Question.Format }}{{ .Config.Icons.Question.Text }} {{color "reset"}}
59+
{{- color "default+hb"}}{{ .Message }}{{ .FilterMessage }}{{color "reset"}}
60+
{{- if .ShowAnswer}}{{color "cyan"}} {{.Answer}}{{color "reset"}}{{"\n"}}
61+
{{- else}}
62+
{{- " "}}{{- color "cyan"}}[Use arrows to move, type to filter{{- if and .Help (not .ShowHelp)}}, {{ .Config.HelpInput }} for more help{{end}}]{{color "reset"}}
63+
{{- "\n"}}
64+
%s{{- "\n"}}
65+
{{- range $ix, $option := .PageEntries}}
66+
{{- template "option" $.IterateOption $ix $option}}
67+
{{- end}}
68+
{{- end}}`, header)
69+
70+
var out string
71+
err := survey.AskOne(&survey.Select{
72+
Message: promptMessage,
73+
Options: options,
74+
Filter: filterMultiToken,
75+
}, &out)
76+
if err != nil {
77+
return nil, err
78+
}
79+
80+
survey.SelectQuestionTemplate = originalSelectTemplate
81+
82+
return optionsMap[out], nil
83+
}

pkg/granted/rds/rds.go

Lines changed: 1 addition & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@ import (
77

88
"connectrpc.com/connect"
99

10-
"github.com/charmbracelet/huh"
11-
"github.com/charmbracelet/lipgloss"
1210
"github.com/common-fate/clio"
1311
"github.com/common-fate/grab"
1412
"github.com/common-fate/granted/pkg/cfcfg"
@@ -17,7 +15,6 @@ import (
1715
accessv1alpha1 "github.com/common-fate/sdk/gen/commonfate/access/v1alpha1"
1816
"github.com/common-fate/sdk/service/access"
1917
"github.com/fatih/color"
20-
"github.com/mattn/go-runewidth"
2118

2219
"github.com/urfave/cli/v2"
2320
)
@@ -162,52 +159,8 @@ func promptForDatabaseAndUser(ctx context.Context, cfg *config.Context) (*access
162159
return nil, errors.New("you don't have access to any RDS databases")
163160
}
164161

165-
type Column struct {
166-
Title string
167-
Width int
168-
}
169-
cols := []Column{{Title: "Database", Width: 40}, {Title: "Role", Width: 40}}
170-
var s = make([]string, 0, len(cols))
171-
for _, col := range cols {
172-
style := lipgloss.NewStyle().Width(col.Width).MaxWidth(col.Width).Inline(true)
173-
renderedCell := style.Render(runewidth.Truncate(col.Title, col.Width, "…"))
174-
s = append(s, lipgloss.NewStyle().Bold(true).Padding(0).Render(renderedCell))
175-
}
176-
header := lipgloss.NewStyle().PaddingLeft(2).Render(lipgloss.JoinHorizontal(lipgloss.Left, s...))
177-
var options []huh.Option[*accessv1alpha1.Entitlement]
178-
179-
for _, entitlement := range entitlements {
180-
style := lipgloss.NewStyle().Width(cols[0].Width).MaxWidth(cols[0].Width).Inline(true)
181-
target := lipgloss.NewStyle().Bold(true).Padding(0).Render(style.Render(runewidth.Truncate(entitlement.Target.Display(), cols[0].Width, "…")))
182-
183-
style = lipgloss.NewStyle().Width(cols[1].Width).MaxWidth(cols[1].Width).Inline(true)
184-
role := lipgloss.NewStyle().Bold(true).Padding(0).Render(style.Render(runewidth.Truncate(entitlement.Role.Display(), cols[1].Width, "…")))
185-
186-
options = append(options, huh.Option[*accessv1alpha1.Entitlement]{
187-
Key: lipgloss.JoinHorizontal(lipgloss.Left, target, role),
188-
Value: entitlement,
189-
})
190-
}
191-
192-
selector := huh.NewSelect[*accessv1alpha1.Entitlement]().
193-
// show the filter dialog when there are 2 or more options
194-
Filtering(len(options) > 1).
195-
Options(options...).
196-
Title("Select a database to connect to").
197-
Description(header).WithTheme(huh.ThemeBase())
198-
199-
err = selector.Run()
200-
if err != nil {
201-
return nil, err
202-
}
203-
204-
selectorVal := selector.GetValue()
205-
206-
if selectorVal == nil {
207-
return nil, errors.New("no database selected")
208-
}
162+
return proxy.PromptEntitlements(entitlements, "Database", "Role", "Select a database to connect to: ")
209163

210-
return selectorVal.(*accessv1alpha1.Entitlement), nil
211164
}
212165

213166
func clientConnectionParameters(c *cli.Context, ensuredAccess *proxy.EnsureAccessOutput[*accessv1alpha1.AWSRDSOutput]) (connectionString, cliString string, port int, err error) {

0 commit comments

Comments
 (0)