bridge/github: configuration with global configs

amine created

Change summary

bridge/github/config.go | 112 +++++++++++++++++++++++++++++++++++++-----
1 file changed, 98 insertions(+), 14 deletions(-)

Detailed changes

bridge/github/config.go 🔗

@@ -21,6 +21,7 @@ import (
 	"golang.org/x/crypto/ssh/terminal"
 
 	"github.com/MichaelMure/git-bug/bridge/core"
+	"github.com/MichaelMure/git-bug/entity"
 	"github.com/MichaelMure/git-bug/repository"
 	"github.com/MichaelMure/git-bug/util/interrupt"
 )
@@ -43,10 +44,12 @@ func (g *Github) Configure(repo repository.RepoCommon, params core.BridgeParams)
 	conf := make(core.Configuration)
 	var err error
 	var token string
+	var tokenId entity.Id
+	var tokenObj *core.Token
 	var owner string
 	var project string
 
-	if (params.Token != "" || params.TokenStdin) &&
+	if (params.Token != "" || params.TokenId != "" || params.TokenStdin) &&
 		(params.URL == "" && (params.Project == "" || params.Owner == "")) {
 		return nil, fmt.Errorf("you must provide a project URL or Owner/Name to configure this bridge with a token")
 	}
@@ -87,11 +90,11 @@ func (g *Github) Configure(repo repository.RepoCommon, params core.BridgeParams)
 		return nil, fmt.Errorf("invalid parameter owner: %v", owner)
 	}
 
-	// try to get token from params if provided, else use terminal prompt to either
-	// enter a token or login and generate a new one
+	// try to get token from params if provided, else use terminal prompt
+	// to either enter a token or login and generate a new one, or choose
+	// an existing token
 	if params.Token != "" {
 		token = params.Token
-
 	} else if params.TokenStdin {
 		reader := bufio.NewReader(os.Stdin)
 		token, err = reader.ReadString('\n')
@@ -99,15 +102,33 @@ func (g *Github) Configure(repo repository.RepoCommon, params core.BridgeParams)
 			return nil, fmt.Errorf("reading from stdin: %v", err)
 		}
 		token = strings.TrimSuffix(token, "\n")
+	} else if params.TokenId != "" {
+		tokenId = entity.Id(params.TokenId)
 	} else {
-		token, err = promptTokenOptions(owner, project)
+		tokenObj, err = promptTokenOptions(repo, owner, project)
+		if err != nil {
+			return nil, err
+		}
+	}
+
+	// at this point, we check if the token already exist or we create a new one
+	if token != "" {
+		tokenObj, err = loadOrCreateToken(repo, token)
 		if err != nil {
 			return nil, err
 		}
+	} else if tokenId != "" {
+		tokenObj, err = core.LoadToken(repo, entity.Id(tokenId))
+		if err != nil {
+			return nil, err
+		}
+		if tokenObj.Target != target {
+			return nil, fmt.Errorf("token target is incompatible %s", tokenObj.Target)
+		}
 	}
 
 	// verify access to the repository with token
-	ok, err = validateProject(owner, project, token)
+	ok, err = validateProject(owner, project, tokenObj.Value)
 	if err != nil {
 		return nil, err
 	}
@@ -116,7 +137,7 @@ func (g *Github) Configure(repo repository.RepoCommon, params core.BridgeParams)
 	}
 
 	conf[core.ConfigKeyTarget] = target
-	conf[keyToken] = token
+	conf[core.ConfigKeyTokenId] = tokenObj.ID().String()
 	conf[keyOwner] = owner
 	conf[keyProject] = project
 
@@ -128,6 +149,27 @@ func (g *Github) Configure(repo repository.RepoCommon, params core.BridgeParams)
 	return conf, nil
 }
 
+func loadOrCreateToken(repo repository.RepoCommon, tokenValue string) (*core.Token, error) {
+	tokens, err := core.LoadTokens(repo)
+	if err != nil {
+		return nil, err
+	}
+
+	for _, token := range tokens {
+		if token.Target == target && token.Value == tokenValue {
+			return token, nil
+		}
+	}
+
+	token := core.NewToken(tokenValue, target)
+	err = core.StoreToken(repo, token)
+	if err != nil {
+		return nil, err
+	}
+
+	return token, nil
+}
+
 func (*Github) ValidateConfig(conf core.Configuration) error {
 	if v, ok := conf[core.ConfigKeyTarget]; !ok {
 		return fmt.Errorf("missing %s key", core.ConfigKeyTarget)
@@ -135,8 +177,8 @@ func (*Github) ValidateConfig(conf core.Configuration) error {
 		return fmt.Errorf("unexpected target name: %v", v)
 	}
 
-	if _, ok := conf[keyToken]; !ok {
-		return fmt.Errorf("missing %s key", keyToken)
+	if _, ok := conf[core.ConfigKeyTokenId]; !ok {
+		return fmt.Errorf("missing %s key", core.ConfigKeyTokenId)
 	}
 
 	if _, ok := conf[keyOwner]; !ok {
@@ -220,35 +262,77 @@ func randomFingerprint() string {
 	return string(b)
 }
 
-func promptTokenOptions(owner, project string) (string, error) {
+func promptTokenOptions(repo repository.RepoCommon, owner, project string) (*core.Token, error) {
 	for {
+		tokens, err := core.LoadTokens(repo)
+		if err != nil {
+			return nil, err
+		}
+
 		fmt.Println()
 		fmt.Println("[1]: user provided token")
 		fmt.Println("[2]: interactive token creation")
+		fmt.Println("known tokens for Github:")
+
+		var githubTokens []*core.Token
+		i := 0
+		for _, token := range tokens {
+			if token.Target == target {
+				fmt.Printf("[%d]: %s\n", i+3, token.ID())
+				githubTokens = append(githubTokens, token)
+				i++
+			}
+		}
 		fmt.Print("Select option: ")
 
 		line, err := bufio.NewReader(os.Stdin).ReadString('\n')
 		fmt.Println()
 		if err != nil {
-			return "", err
+			return nil, err
 		}
 
 		line = strings.TrimRight(line, "\n")
 
 		index, err := strconv.Atoi(line)
-		if err != nil || (index != 1 && index != 2) {
+		if err != nil || index < 1 || index > len(githubTokens)+2 {
 			fmt.Println("invalid input")
 			continue
 		}
 
+		var token string
 		if index == 1 {
-			return promptToken()
+			token, err = promptToken()
+			if err != nil {
+				return nil, err
+			}
+		} else if index == 2 {
+			token, err = loginAndRequestToken(owner, project)
+			if err != nil {
+				return nil, err
+			}
+		} else {
+			return githubTokens[index-2], nil
 		}
 
-		return loginAndRequestToken(owner, project)
+		return loadOrCreateToken(repo, token)
 	}
 }
 
+func tokenAlreadyExist(repo repository.RepoCommon, id string) (bool, error) {
+	tokens, err := core.LoadTokens(repo)
+	if err != nil {
+		return false, err
+	}
+
+	for _, token := range tokens {
+		if token.Target == target && token.Value == id {
+			return true, nil
+		}
+	}
+
+	return false, nil
+}
+
 func promptToken() (string, error) {
 	fmt.Println("You can generate a new token by visiting https://github.com/settings/tokens.")
 	fmt.Println("Choose 'Generate new token' and set the necessary access scope for your repository.")