Merge pull request #326 from MichaelMure/bridge-refactor

Michael Muré created

bridges: more refactor and cleanup

Change summary

bridge/core/auth/credential.go |  2 
bridge/core/bridge.go          |  4 
bridge/core/config.go          |  5 ++
bridge/github/config.go        | 35 +++++--------
bridge/github/github.go        |  6 +-
bridge/gitlab/config.go        | 14 +++--
input/prompt.go                | 88 ++++++++---------------------------
7 files changed, 54 insertions(+), 100 deletions(-)

Detailed changes

bridge/core/auth/credential.go 🔗

@@ -123,7 +123,7 @@ func loadFromConfig(rawConfigs map[string]string, id entity.Id) (Credential, err
 	case KindLoginPassword:
 		cred, err = NewLoginPasswordFromConfig(configs)
 	default:
-		return nil, fmt.Errorf("unknown credential type %s", configs[configKeyKind])
+		return nil, fmt.Errorf("unknown credential type \"%s\"", configs[configKeyKind])
 	}
 
 	if err != nil {

bridge/core/bridge.go 🔗

@@ -52,7 +52,7 @@ func Register(impl BridgeImpl) {
 	if bridgeLoginMetaKey == nil {
 		bridgeLoginMetaKey = make(map[string]string)
 	}
-	bridgeImpl[impl.Target()] = reflect.TypeOf(impl)
+	bridgeImpl[impl.Target()] = reflect.TypeOf(impl).Elem()
 	bridgeLoginMetaKey[impl.Target()] = impl.LoginMetaKey()
 }
 
@@ -94,7 +94,7 @@ func NewBridge(repo *cache.RepoCache, target string, name string) (*Bridge, erro
 		return nil, fmt.Errorf("unknown bridge target %v", target)
 	}
 
-	impl := reflect.New(implType).Elem().Interface().(BridgeImpl)
+	impl := reflect.New(implType).Interface().(BridgeImpl)
 
 	bridge := &Bridge{
 		Name: name,

bridge/core/config.go 🔗

@@ -1,6 +1,8 @@
 package core
 
 import (
+	"fmt"
+
 	"github.com/MichaelMure/git-bug/cache"
 	"github.com/MichaelMure/git-bug/identity"
 )
@@ -24,6 +26,7 @@ func FinishConfig(repo *cache.RepoCache, metaKey string, login string) error {
 		return err
 	}
 	if err == nil {
+		fmt.Printf("Current identity %v tagged with login %v\n", user.Id().Human(), login)
 		// found one
 		user.SetMetadata(metaKey, login)
 		return user.CommitAsNeeded()
@@ -42,5 +45,7 @@ func FinishConfig(repo *cache.RepoCache, metaKey string, login string) error {
 		return err
 	}
 
+	fmt.Printf("Identity %v created, set as current\n", i.Id().Human())
+
 	return nil
 }

bridge/github/config.go 🔗

@@ -143,7 +143,7 @@ func (g *Github) Configure(repo *cache.RepoCache, params core.BridgeParams) (cor
 	return conf, core.FinishConfig(repo, metaKeyGithubLogin, login)
 }
 
-func (Github) ValidateConfig(conf core.Configuration) error {
+func (*Github) ValidateConfig(conf core.Configuration) error {
 	if v, ok := conf[core.ConfigKeyTarget]; !ok {
 		return fmt.Errorf("missing %s key", core.ConfigKeyTarget)
 	} else if v != target {
@@ -252,13 +252,18 @@ func promptTokenOptions(repo repository.RepoConfig, login, owner, project string
 		return nil, err
 	}
 
-	cred, err := input.PromptCredentialWithInteractive(target, "token", creds)
-	switch err {
-	case nil:
+	cred, index, err := input.PromptCredential(target, "token", creds, []string{
+		"enter my token",
+		"interactive token creation",
+	})
+	switch {
+	case err != nil:
+		return nil, err
+	case cred != nil:
 		return cred, nil
-	case input.ErrDirectPrompt:
+	case index == 0:
 		return promptToken()
-	case input.ErrInteractiveCreation:
+	case index == 1:
 		value, err := loginAndRequestToken(login, owner, project)
 		if err != nil {
 			return nil, err
@@ -267,7 +272,7 @@ func promptTokenOptions(repo repository.RepoConfig, login, owner, project string
 		token.SetMetadata(auth.MetaKeyLogin, login)
 		return token, nil
 	default:
-		return nil, err
+		panic("missed case")
 	}
 }
 
@@ -322,31 +327,19 @@ func loginAndRequestToken(login, owner, project string) (string, error) {
 	fmt.Println()
 
 	// prompt project visibility to know the token scope needed for the repository
-	i, err := input.PromptChoice("repository visibility", []string{"public", "private"})
+	index, err := input.PromptChoice("repository visibility", []string{"public", "private"})
 	if err != nil {
 		return "", err
 	}
-	isPublic := i == 0
+	scope := []string{"public_repo", "repo"}[index]
 
 	password, err := input.PromptPassword("Password", "password", input.Required)
 	if err != nil {
 		return "", err
 	}
 
-	var scope string
-	if isPublic {
-		// public_repo is requested to be able to read public repositories
-		scope = "public_repo"
-	} else {
-		// 'repo' is request to be able to read private repositories
-		// /!\ token will have read/write rights on every private repository you have access to
-		scope = "repo"
-	}
-
 	// Attempt to authenticate and create a token
-
 	note := fmt.Sprintf("git-bug - %s/%s", owner, project)
-
 	resp, err := requestToken(note, login, password, scope)
 	if err != nil {
 		return "", err

bridge/github/github.go 🔗

@@ -30,7 +30,7 @@ var _ core.BridgeImpl = &Github{}
 
 type Github struct{}
 
-func (Github) Target() string {
+func (*Github) Target() string {
 	return target
 }
 
@@ -38,11 +38,11 @@ func (g *Github) LoginMetaKey() string {
 	return metaKeyGithubLogin
 }
 
-func (Github) NewImporter() core.Importer {
+func (*Github) NewImporter() core.Importer {
 	return &githubImporter{}
 }
 
-func (Github) NewExporter() core.Exporter {
+func (*Github) NewExporter() core.Exporter {
 	return &githubExporter{}
 }
 

bridge/gitlab/config.go 🔗

@@ -161,14 +161,18 @@ func promptTokenOptions(repo repository.RepoConfig, login, baseUrl string) (auth
 		return nil, err
 	}
 
-	cred, err := input.PromptCredential(target, "token", creds)
-	switch err {
-	case nil:
+	cred, index, err := input.PromptCredential(target, "token", creds, []string{
+		"enter my token",
+	})
+	switch {
+	case err != nil:
+		return nil, err
+	case cred != nil:
 		return cred, nil
-	case input.ErrDirectPrompt:
+	case index == 0:
 		return promptToken(baseUrl)
 	default:
-		return nil, err
+		panic("missed case")
 	}
 }
 

input/prompt.go 🔗

@@ -2,7 +2,6 @@ package input
 
 import (
 	"bufio"
-	"errors"
 	"fmt"
 	"net/url"
 	"os"
@@ -47,6 +46,8 @@ func IsURL(name string, value string) (string, error) {
 	return "", nil
 }
 
+// Prompts
+
 func Prompt(prompt, name string, validators ...PromptValidator) (string, error) {
 	return PromptDefault(prompt, name, "", validators...)
 }
@@ -148,7 +149,7 @@ func PromptChoice(prompt string, choices []string) (int, error) {
 			continue
 		}
 
-		return index, nil
+		return index - 1, nil
 	}
 }
 
@@ -193,69 +194,22 @@ func PromptURLWithRemote(prompt, name string, validRemotes []string, validators
 	return Prompt(prompt, name, validators...)
 }
 
-var ErrDirectPrompt = errors.New("direct prompt selected")
-var ErrInteractiveCreation = errors.New("interactive creation selected")
-
-func PromptCredential(target, name string, credentials []auth.Credential) (auth.Credential, error) {
-	if len(credentials) == 0 {
-		return nil, nil
+func PromptCredential(target, name string, credentials []auth.Credential, choices []string) (auth.Credential, int, error) {
+	if len(credentials) == 0 && len(choices) == 0 {
+		return nil, 0, fmt.Errorf("no possible choice")
 	}
-
-	sort.Sort(auth.ById(credentials))
-
-	for {
-		_, _ = fmt.Fprintf(os.Stderr, "[1]: enter my %s\n", name)
-
-		_, _ = fmt.Fprintln(os.Stderr)
-		_, _ = fmt.Fprintf(os.Stderr, "Existing %s for %s:", name, target)
-
-		for i, cred := range credentials {
-			meta := make([]string, 0, len(cred.Metadata()))
-			for k, v := range cred.Metadata() {
-				meta = append(meta, k+":"+v)
-			}
-			sort.Strings(meta)
-			metaFmt := strings.Join(meta, ",")
-
-			fmt.Printf("[%d]: %s => (%s) (%s)\n",
-				i+2,
-				colors.Cyan(cred.ID().Human()),
-				metaFmt,
-				cred.CreateTime().Format(time.RFC822),
-			)
-		}
-
-		_, _ = fmt.Fprintln(os.Stderr)
-		_, _ = fmt.Fprintf(os.Stderr, "Select option: ")
-
-		line, err := bufio.NewReader(os.Stdin).ReadString('\n')
-		_, _ = fmt.Fprintln(os.Stderr)
-		if err != nil {
-			return nil, err
-		}
-
-		line = strings.TrimSpace(line)
-		index, err := strconv.Atoi(line)
-		if err != nil || index < 1 || index > len(credentials)+1 {
-			_, _ = fmt.Fprintln(os.Stderr, "invalid input")
-			continue
-		}
-
-		switch index {
-		case 1:
-			return nil, ErrDirectPrompt
-		default:
-			return credentials[index-2], nil
-		}
+	if len(credentials) == 0 && len(choices) == 1 {
+		return nil, 0, nil
 	}
-}
 
-func PromptCredentialWithInteractive(target, name string, credentials []auth.Credential) (auth.Credential, error) {
 	sort.Sort(auth.ById(credentials))
 
 	for {
-		_, _ = fmt.Fprintf(os.Stderr, "[1]: enter my %s\n", name)
-		_, _ = fmt.Fprintf(os.Stderr, "[2]: interactive %s creation\n", name)
+		offset := 0
+		for i, choice := range choices {
+			_, _ = fmt.Fprintf(os.Stderr, "[%d]: %s\n", i+1, choice)
+			offset++
+		}
 
 		if len(credentials) > 0 {
 			_, _ = fmt.Fprintln(os.Stderr)
@@ -270,7 +224,7 @@ func PromptCredentialWithInteractive(target, name string, credentials []auth.Cre
 				metaFmt := strings.Join(meta, ",")
 
 				fmt.Printf("[%d]: %s => (%s) (%s)\n",
-					i+2,
+					i+1+offset,
 					colors.Cyan(cred.ID().Human()),
 					metaFmt,
 					cred.CreateTime().Format(time.RFC822),
@@ -284,23 +238,21 @@ func PromptCredentialWithInteractive(target, name string, credentials []auth.Cre
 		line, err := bufio.NewReader(os.Stdin).ReadString('\n')
 		_, _ = fmt.Fprintln(os.Stderr)
 		if err != nil {
-			return nil, err
+			return nil, 0, err
 		}
 
 		line = strings.TrimSpace(line)
 		index, err := strconv.Atoi(line)
-		if err != nil || index < 1 || index > len(credentials)+1 {
+		if err != nil || index < 1 || index > len(choices)+len(credentials) {
 			_, _ = fmt.Fprintln(os.Stderr, "invalid input")
 			continue
 		}
 
-		switch index {
-		case 1:
-			return nil, ErrDirectPrompt
-		case 2:
-			return nil, ErrInteractiveCreation
+		switch {
+		case index < len(choices):
+			return nil, index - 1, nil
 		default:
-			return credentials[index-3], nil
+			return credentials[index-len(choices)-1], 0, nil
 		}
 	}
 }