Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

workspace/new + tests #353

Merged
merged 3 commits into from
Nov 15, 2024
Merged
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
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