Skip to content

Commit

Permalink
Merge pull request #353 from rusq/ui-4
Browse files Browse the repository at this point in the history
workspace/new + tests
  • Loading branch information
rusq authored Nov 15, 2024
2 parents b4ac948 + 6ee01b7 commit d6db24a
Show file tree
Hide file tree
Showing 31 changed files with 559 additions and 108 deletions.
9 changes: 9 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,12 @@ goreleaser:

tags:
gotags -R *.go > $@

generate: | install_tools
go generate ./...
.PHONY:generate

install_tools:
go install go.uber.org/mock/mockgen@latest
go install golang.org/x/tools/cmd/stringer@latest
.PHONY: install_tools
16 changes: 13 additions & 3 deletions auth/auth_ui/auth_ui.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package auth_ui

import (
"errors"
"fmt"
"net/url"
"strings"
)
Expand All @@ -20,9 +22,11 @@ const (
LCancel
)

var ErrInvalidDomain = errors.New("invalid domain")

// Sanitize takes a workspace name or URL and returns the workspace name.
func Sanitize(workspace string) (string, error) {
if !strings.Contains(workspace, ".slack.com") {
if !strings.Contains(workspace, ".slack.com") && !strings.Contains(workspace, ".") {
return workspace, nil
}
if strings.HasPrefix(workspace, "https://") {
Expand All @@ -33,6 +37,12 @@ func Sanitize(workspace string) (string, error) {
workspace = uri.Host
}
// parse
parts := strings.Split(workspace, ".")
return parts[0], nil
name, domain, found := strings.Cut(workspace, ".")
if !found {
return "", errors.New("workspace name is empty")
}
if strings.TrimRight(domain, "/") != "slack.com" {
return "", fmt.Errorf("%s: %w", domain, ErrInvalidDomain)
}
return name, nil
}
1 change: 1 addition & 0 deletions auth/auth_ui/auth_ui_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ func TestSanitize(t *testing.T) {
{"url no slash", args{"https://blahblah.slack.com"}, "blahblah", false},
{"url no schema slash", args{"blahblah.slack.com/"}, "blahblah", false},
{"url no schema no slash", args{"blahblah.slack.com"}, "blahblah", false},
{"not a slack domain", args{"blahblah.example.com"}, "", true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand Down
44 changes: 29 additions & 15 deletions auth/auth_ui/huh.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package auth_ui

import (
"context"
"errors"
"fmt"
"io"
"regexp"
"strconv"

"github.com/charmbracelet/bubbles/key"
"github.com/charmbracelet/huh"
"github.com/rusq/slackauth"
)
Expand All @@ -33,7 +35,7 @@ func (h *Huh) RequestWorkspace(w io.Writer) (string, error) {

func (*Huh) Stop() {}

func (*Huh) RequestCreds(w io.Writer, workspace string) (email string, passwd string, err error) {
func (*Huh) RequestCreds(ctx context.Context, w io.Writer, workspace string) (email string, passwd string, err error) {
f := huh.NewForm(
huh.NewGroup(
huh.NewInput().
Expand All @@ -46,8 +48,8 @@ func (*Huh) RequestCreds(w io.Writer, workspace string) (email string, passwd st
Placeholder("your slack password").
Validate(valRequired).EchoMode(huh.EchoModePassword),
),
).WithTheme(Theme)
err = f.Run()
).WithTheme(Theme).WithKeyMap(keymap)
err = f.RunWithContext(ctx)
return
}

Expand Down Expand Up @@ -85,15 +87,13 @@ type LoginOpts struct {
BrowserPath string
}

func valWorkspace(s string) error {
if err := valRequired(s); err != nil {
return err
}
_, err := Sanitize(s)
return err
var keymap = huh.NewDefaultKeyMap()

func init() {
keymap.Quit = key.NewBinding(key.WithKeys("esc", "ctrl+c"), key.WithHelp("esc", "Quit"))
}

func (*Huh) RequestLoginType(w io.Writer, workspace string) (LoginOpts, error) {
func (*Huh) RequestLoginType(ctx context.Context, w io.Writer, workspace string) (LoginOpts, error) {
var ret = LoginOpts{
Workspace: workspace,
Type: LInteractive,
Expand All @@ -120,7 +120,11 @@ func (*Huh) RequestLoginType(w io.Writer, workspace string) (LoginOpts, error) {

fields = append(fields, huh.NewSelect[LoginType]().
TitleFunc(func() string {
return fmt.Sprintf("Select login type for [%s]", ret.Workspace)
wsp, err := Sanitize(ret.Workspace)
if err != nil {
return "Select login type"
}
return fmt.Sprintf("Select login type for [%s]", wsp)
}, &ret.Workspace).
Options(opts...).
Value(&ret.Type).
Expand All @@ -139,21 +143,31 @@ func (*Huh) RequestLoginType(w io.Writer, workspace string) (LoginOpts, error) {
return ""
}
}, &ret.Type))
if err := huh.NewForm(huh.NewGroup(fields...)).WithTheme(Theme).Run(); err != nil {

form := huh.NewForm(huh.NewGroup(fields...)).WithTheme(Theme).WithKeyMap(keymap)

if err := form.RunWithContext(ctx); err != nil {
return ret, err
}
var err error
ret.Workspace, err = Sanitize(ret.Workspace)
if err != nil {
return ret, err
}

if ret.Type == LUserBrowser {
path, err := chooseBrowser()
path, err := chooseBrowser(ctx)
if err != nil {
return ret, err
}
ret.BrowserPath = path
return ret, err
}

return ret, nil
}

func chooseBrowser() (string, error) {
func chooseBrowser(ctx context.Context) (string, error) {
browsers, err := slackauth.ListBrowsers()
if err != nil {
return "", err
Expand All @@ -172,7 +186,7 @@ func chooseBrowser() (string, error) {
DescriptionFunc(func() string {
return browsers[selection].Path
}, &selection),
)).WithTheme(Theme).Run()
)).WithTheme(Theme).WithKeyMap(keymap).RunWithContext(ctx)
if err != nil {
return "", err
}
Expand Down
8 changes: 8 additions & 0 deletions auth/auth_ui/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,3 +100,11 @@ func valSepEaster() func(v LoginType) error {
return nil
}
}

func valWorkspace(s string) error {
if err := valRequired(s); err != nil {
return err
}
_, err := Sanitize(s)
return err
}
17 changes: 9 additions & 8 deletions auth/browser/playwright_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions auth/rod.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,10 @@ type browserAuthUIExt interface {
// RequestLoginType should request the login type from the user and return
// one of the [auth_ui.LoginType] constants. The implementation should
// provide a way to cancel the login flow, returning [auth_ui.LoginCancel].
RequestLoginType(w io.Writer, workspace string) (auth_ui.LoginOpts, error)
RequestLoginType(ctx context.Context, w io.Writer, workspace string) (auth_ui.LoginOpts, error)
// RequestCreds should request the user's email and password and return
// them.
RequestCreds(w io.Writer, workspace string) (email string, passwd string, err error)
RequestCreds(ctx context.Context, w io.Writer, workspace string) (email string, passwd string, err error)
// ConfirmationCode should request the confirmation code from the user and
// return it.
ConfirmationCode(email string) (code int, err error)
Expand Down Expand Up @@ -95,7 +95,7 @@ func NewRODAuth(ctx context.Context, opts ...Option) (RodAuth, error) {
r.opts.workspace = wsp
}

resp, err := r.opts.ui.RequestLoginType(os.Stdout, r.opts.workspace)
resp, err := r.opts.ui.RequestLoginType(ctx, os.Stdout, r.opts.workspace)
if err != nil {
return r, err
}
Expand Down Expand Up @@ -142,7 +142,7 @@ func NewRODAuth(ctx context.Context, opts ...Option) (RodAuth, error) {
}

func headlessFlow(ctx context.Context, cl *slackauth.Client, workspace string, ui browserAuthUIExt) (sp simpleProvider, err error) {
username, password, err := ui.RequestCreds(os.Stdout, workspace)
username, password, err := ui.RequestCreds(ctx, os.Stdout, workspace)
if err != nil {
return sp, err
}
Expand Down
2 changes: 2 additions & 0 deletions clienter_mock_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions cmd/slackdump/internal/emoji/emojidl/emoji_mock_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions cmd/slackdump/internal/list/mocks_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 26 additions & 0 deletions cmd/slackdump/internal/ui/bubbles/datepicker/focus_string.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions cmd/slackdump/internal/ui/bubbles/menu/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,18 @@ func (m *Model) View() string {
return m.view()
}

func (m *Model) Select(id string) {
if id == m.items[m.cursor].ID {
return
}
for i, item := range m.items {
if item.ID == id && !item.Separator {
m.cursor = i
break
}
}
}

func capfirst(s string) string {
if s == "" {
return ""
Expand Down
3 changes: 1 addition & 2 deletions cmd/slackdump/internal/workspace/del.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,7 @@ Use ` + "`del`" + ` to delete the Slack Workspace login information ("forget"
the workspace).
If the workspace login information is deleted, you will need to login into that
workspace again by running ` + " `slackdump auth new <name>`" + `, in case you
need to use this workspace again.
workspace again by running ` + " `slackdump workspace new <name>`" + `.
Slackdump will ask for the confirmation before deleting. To omit the
question, use ` + "`-y`" + ` flag.
Expand Down
1 change: 1 addition & 0 deletions cmd/slackdump/internal/workspace/mocks_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion cmd/slackdump/internal/workspace/select.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ var CmdWspSelect = &base.Command{
that you have previously authenticated in.
"Current" means that this workspace will be used by default when running
other commands, unless you specify a different workspace explicitly.
other commands, unless you specify a different workspace explicitly with
the ` + "`-w`" + ` flag.
To get the full list of authenticated workspaces, run:
Expand Down
4 changes: 2 additions & 2 deletions cmd/slackdump/internal/workspace/workspace.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,9 @@ automatically detected to be:
},
}

//go:generate mockgen -destination=mocks_test.go -package=workspace -source=workspace.go manager

// manager is used for test rigging.
//
//go:generate mockgen -destination=mocks_test.go -package=workspace -source=workspace.go manager
type manager interface {
Auth(ctx context.Context, name string, c cache.Credentials) (auth.Provider, error)
Delete(name string) error
Expand Down
Loading

0 comments on commit d6db24a

Please sign in to comment.