diff --git a/bridge/github/config.go b/bridge/github/config.go index b6f69d7406626e0431fe7839014ca470a2635595..a7af3c02e4dccca611924e7744c95c66761af403 100644 --- a/bridge/github/config.go +++ b/bridge/github/config.go @@ -126,6 +126,7 @@ func (g *Github) Configure(repo *cache.RepoCache, params core.BridgeParams) (cor conf[core.ConfigKeyTarget] = target conf[confKeyOwner] = owner conf[confKeyProject] = project + conf[confKeyDefaultLogin] = login err = g.ValidateConfig(conf) if err != nil { @@ -149,14 +150,15 @@ func (*Github) ValidateConfig(conf core.Configuration) error { } else if v != target { return fmt.Errorf("unexpected target name: %v", v) } - if _, ok := conf[confKeyOwner]; !ok { return fmt.Errorf("missing %s key", confKeyOwner) } - if _, ok := conf[confKeyProject]; !ok { return fmt.Errorf("missing %s key", confKeyProject) } + if _, ok := conf[confKeyDefaultLogin]; !ok { + return fmt.Errorf("missing %s key", confKeyDefaultLogin) + } return nil } diff --git a/bridge/github/export.go b/bridge/github/export.go index 12b62fa6af3b97f035e40f466b1676a04c6720a6..b939d878f526779da71a6b3ad5d1058375164238 100644 --- a/bridge/github/export.go +++ b/bridge/github/export.go @@ -35,10 +35,13 @@ type githubExporter struct { identityClient map[entity.Id]*githubv4.Client // the client to use for non user-specific queries - // should be the client of the default user + // it's the client associated to the "default-login" config + // used for the github V4 API (graphql) defaultClient *githubv4.Client // the token of the default user + // it's the token associated to the "default-login" config + // used for the github V3 API (REST) defaultToken *auth.Token // github repository ID @@ -59,34 +62,12 @@ func (ge *githubExporter) Init(_ context.Context, repo *cache.RepoCache, conf co ge.cachedOperationIDs = make(map[entity.Id]string) ge.cachedLabels = make(map[string]string) - user, err := repo.GetUserIdentity() - if err != nil { - return err - } - // preload all clients - err = ge.cacheAllClient(repo) + err := ge.cacheAllClient(repo) if err != nil { return err } - ge.defaultClient, err = ge.getClientForIdentity(user.Id()) - if err != nil { - return err - } - - login := user.ImmutableMetadata()[metaKeyGithubLogin] - creds, err := auth.List(repo, auth.WithMeta(auth.MetaKeyLogin, login), auth.WithTarget(target), auth.WithKind(auth.KindToken)) - if err != nil { - return err - } - - if len(creds) == 0 { - return ErrMissingIdentityToken - } - - ge.defaultToken = creds[0].(*auth.Token) - return nil } @@ -111,12 +92,24 @@ func (ge *githubExporter) cacheAllClient(repo *cache.RepoCache) error { return nil } - if _, ok := ge.identityClient[user.Id()]; !ok { - client := buildClient(creds[0].(*auth.Token)) - ge.identityClient[user.Id()] = client + if _, ok := ge.identityClient[user.Id()]; ok { + continue + } + + client := buildClient(creds[0].(*auth.Token)) + ge.identityClient[user.Id()] = client + + // assign the default client and token as well + if ge.defaultClient == nil && login == ge.conf[confKeyDefaultLogin] { + ge.defaultClient = client + ge.defaultToken = creds[0].(*auth.Token) } } + if ge.defaultClient == nil { + return fmt.Errorf("no token found for the default login \"%s\"", ge.conf[confKeyDefaultLogin]) + } + return nil } diff --git a/bridge/github/export_test.go b/bridge/github/export_test.go index acbd657af76ce23bb84f15f8644e6a851d63b0c8..0748ecbf59612e27b91e6207456163acc3b8116c 100644 --- a/bridge/github/export_test.go +++ b/bridge/github/export_test.go @@ -190,8 +190,9 @@ func TestPushPull(t *testing.T) { // initialize exporter exporter := &githubExporter{} err = exporter.Init(ctx, backend, core.Configuration{ - confKeyOwner: envUser, - confKeyProject: projectName, + confKeyOwner: envUser, + confKeyProject: projectName, + confKeyDefaultLogin: login, }) require.NoError(t, err) @@ -217,8 +218,9 @@ func TestPushPull(t *testing.T) { importer := &githubImporter{} err = importer.Init(ctx, backend, core.Configuration{ - confKeyOwner: envUser, - confKeyProject: projectName, + confKeyOwner: envUser, + confKeyProject: projectName, + confKeyDefaultLogin: login, }) require.NoError(t, err) diff --git a/bridge/github/github.go b/bridge/github/github.go index 3a99cec706a962252d147bb711d533aba479a478..1e85eb9a1b747217470d11d1569068670502e8d4 100644 --- a/bridge/github/github.go +++ b/bridge/github/github.go @@ -19,8 +19,9 @@ const ( metaKeyGithubUrl = "github-url" metaKeyGithubLogin = "github-login" - confKeyOwner = "owner" - confKeyProject = "project" + confKeyOwner = "owner" + confKeyProject = "project" + confKeyDefaultLogin = "default-login" githubV3Url = "https://api.github.com" defaultTimeout = 60 * time.Second diff --git a/bridge/github/import.go b/bridge/github/import.go index e80b9cfdffaafbbb672789294b324c2e2325c701..7aec809f78d3a5b45e474c1a21bf715dc03f58a1 100644 --- a/bridge/github/import.go +++ b/bridge/github/import.go @@ -19,7 +19,7 @@ import ( type githubImporter struct { conf core.Configuration - // default user client + // default client client *githubv4.Client // iterator @@ -32,7 +32,11 @@ type githubImporter struct { func (gi *githubImporter) Init(_ context.Context, repo *cache.RepoCache, conf core.Configuration) error { gi.conf = conf - creds, err := auth.List(repo, auth.WithTarget(target), auth.WithKind(auth.KindToken)) + creds, err := auth.List(repo, + auth.WithTarget(target), + auth.WithKind(auth.KindToken), + auth.WithMeta(auth.MetaKeyLogin, conf[confKeyDefaultLogin]), + ) if err != nil { return err } @@ -434,7 +438,7 @@ func (gi *githubImporter) ensureTimelineComment(repo *cache.RepoCache, b *cache. } gi.out <- core.NewImportComment(op.Id()) - // set target for the nexr edit now that the comment is created + // set target for the next edit now that the comment is created targetOpID = op.Id() continue } diff --git a/bridge/github/import_test.go b/bridge/github/import_test.go index 20b1b71e841e4b927cf4bda2bc1a224a38bf7c30..107d74c576bf30f2282ce01f8f909d9baaa8d560 100644 --- a/bridge/github/import_test.go +++ b/bridge/github/import_test.go @@ -153,8 +153,9 @@ func Test_Importer(t *testing.T) { importer := &githubImporter{} err = importer.Init(ctx, backend, core.Configuration{ - confKeyOwner: "MichaelMure", - confKeyProject: "git-bug-test-github-bridge", + confKeyOwner: "MichaelMure", + confKeyProject: "git-bug-test-github-bridge", + confKeyDefaultLogin: login, }) require.NoError(t, err) diff --git a/bridge/gitlab/config.go b/bridge/gitlab/config.go index d5dc418c622be40a28533f4c5a5388dcdf08571c..8d2490b8c9ecfdd50483351c0b320db59aa0647e 100644 --- a/bridge/gitlab/config.go +++ b/bridge/gitlab/config.go @@ -117,6 +117,7 @@ func (g *Gitlab) Configure(repo *cache.RepoCache, params core.BridgeParams) (cor conf[core.ConfigKeyTarget] = target conf[confKeyProjectID] = strconv.Itoa(id) conf[confKeyGitlabBaseUrl] = baseUrl + conf[confKeyDefaultLogin] = login err = g.ValidateConfig(conf) if err != nil { @@ -146,6 +147,9 @@ func (g *Gitlab) ValidateConfig(conf core.Configuration) error { if _, ok := conf[confKeyProjectID]; !ok { return fmt.Errorf("missing %s key", confKeyProjectID) } + if _, ok := conf[confKeyDefaultLogin]; !ok { + return fmt.Errorf("missing %s key", confKeyDefaultLogin) + } return nil } @@ -246,12 +250,12 @@ func getValidGitlabRemoteURLs(repo repository.RepoCommon, baseUrl string) ([]str urls := make([]string, 0, len(remotes)) for _, u := range remotes { - path, err := getProjectPath(baseUrl, u) + p, err := getProjectPath(baseUrl, u) if err != nil { continue } - urls = append(urls, fmt.Sprintf("%s/%s", baseUrl, path)) + urls = append(urls, fmt.Sprintf("%s/%s", baseUrl, p)) } return urls, nil diff --git a/bridge/gitlab/export_test.go b/bridge/gitlab/export_test.go index d704ac3b939804fb5f64b9f1c2a4387fa8ed5d28..d8966163b507a6f9a9c3473ae8e3db13f52f64a5 100644 --- a/bridge/gitlab/export_test.go +++ b/bridge/gitlab/export_test.go @@ -198,6 +198,7 @@ func TestPushPull(t *testing.T) { err = exporter.Init(ctx, backend, core.Configuration{ confKeyProjectID: strconv.Itoa(projectID), confKeyGitlabBaseUrl: defaultBaseURL, + confKeyDefaultLogin: login, }) require.NoError(t, err) @@ -225,6 +226,7 @@ func TestPushPull(t *testing.T) { err = importer.Init(ctx, backend, core.Configuration{ confKeyProjectID: strconv.Itoa(projectID), confKeyGitlabBaseUrl: defaultBaseURL, + confKeyDefaultLogin: login, }) require.NoError(t, err) diff --git a/bridge/gitlab/gitlab.go b/bridge/gitlab/gitlab.go index ec7b7e5756736e2238bff664e204064bd61a75c0..d854f2f1ca7c93144d29c652e65f398679864c83 100644 --- a/bridge/gitlab/gitlab.go +++ b/bridge/gitlab/gitlab.go @@ -21,6 +21,7 @@ const ( confKeyProjectID = "project-id" confKeyGitlabBaseUrl = "base-url" + confKeyDefaultLogin = "default-login" defaultBaseURL = "https://gitlab.com/" defaultTimeout = 60 * time.Second diff --git a/bridge/gitlab/import.go b/bridge/gitlab/import.go index 5faf5c48bf5927142f1f40307343c4052901c0c5..9ebb0d882e29f404fe9d8dd5012cf65772781fea 100644 --- a/bridge/gitlab/import.go +++ b/bridge/gitlab/import.go @@ -20,7 +20,7 @@ import ( type gitlabImporter struct { conf core.Configuration - // default user client + // default client client *gitlab.Client // iterator @@ -37,6 +37,7 @@ func (gi *gitlabImporter) Init(_ context.Context, repo *cache.RepoCache, conf co auth.WithTarget(target), auth.WithKind(auth.KindToken), auth.WithMeta(auth.MetaKeyBaseURL, conf[confKeyGitlabBaseUrl]), + auth.WithMeta(auth.MetaKeyLogin, conf[confKeyDefaultLogin]), ) if err != nil { return err diff --git a/bridge/gitlab/import_test.go b/bridge/gitlab/import_test.go index ea7acc18658b708d6a5cae6a17c10ade9860a7ff..f916d20cb701dbdf030afbe4662931815f8134dd 100644 --- a/bridge/gitlab/import_test.go +++ b/bridge/gitlab/import_test.go @@ -110,6 +110,7 @@ func TestImport(t *testing.T) { err = importer.Init(ctx, backend, core.Configuration{ confKeyProjectID: projectID, confKeyGitlabBaseUrl: defaultBaseURL, + confKeyDefaultLogin: login, }) require.NoError(t, err) diff --git a/bridge/jira/config.go b/bridge/jira/config.go index 79fd850751a2e9b76af38b958a36620f809ad9a0..1a6ab18a8b1e6eccbb00cae362c184ca3c04b832 100644 --- a/bridge/jira/config.go +++ b/bridge/jira/config.go @@ -79,7 +79,7 @@ func (j *Jira) Configure(repo *cache.RepoCache, params core.BridgeParams) (core. } login = l default: - login := params.Login + login = params.Login if login == "" { // TODO: validate username login, err = input.Prompt("JIRA login", "login", input.Required) @@ -98,6 +98,7 @@ func (j *Jira) Configure(repo *cache.RepoCache, params core.BridgeParams) (core. conf[confKeyBaseUrl] = baseURL conf[confKeyProject] = project conf[confKeyCredentialType] = credType + conf[confKeyDefaultLogin] = login err = j.ValidateConfig(conf) if err != nil { @@ -144,10 +145,18 @@ func (*Jira) ValidateConfig(conf core.Configuration) error { } else if v != target { return fmt.Errorf("unexpected target name: %v", v) } - + if _, ok := conf[confKeyBaseUrl]; !ok { + return fmt.Errorf("missing %s key", confKeyBaseUrl) + } if _, ok := conf[confKeyProject]; !ok { return fmt.Errorf("missing %s key", confKeyProject) } + if _, ok := conf[confKeyCredentialType]; !ok { + return fmt.Errorf("missing %s key", confKeyCredentialType) + } + if _, ok := conf[confKeyDefaultLogin]; !ok { + return fmt.Errorf("missing %s key", confKeyDefaultLogin) + } return nil } diff --git a/bridge/jira/import.go b/bridge/jira/import.go index f35f114f66e81fc880a2779a826b24d23b17657c..21305bd58935e5294778dbc287611cc6940ac5c8 100644 --- a/bridge/jira/import.go +++ b/bridge/jira/import.go @@ -40,8 +40,9 @@ func (ji *jiraImporter) Init(ctx context.Context, repo *cache.RepoCache, conf co // Prioritize LoginPassword credentials to avoid a prompt creds, err := auth.List(repo, auth.WithTarget(target), - auth.WithMeta(auth.MetaKeyBaseURL, conf[confKeyBaseUrl]), auth.WithKind(auth.KindLoginPassword), + auth.WithMeta(auth.MetaKeyBaseURL, conf[confKeyBaseUrl]), + auth.WithMeta(auth.MetaKeyLogin, conf[confKeyDefaultLogin]), ) if err != nil { return err @@ -53,8 +54,9 @@ func (ji *jiraImporter) Init(ctx context.Context, repo *cache.RepoCache, conf co creds, err = auth.List(repo, auth.WithTarget(target), - auth.WithMeta(auth.MetaKeyBaseURL, conf[confKeyBaseUrl]), auth.WithKind(auth.KindLogin), + auth.WithMeta(auth.MetaKeyBaseURL, conf[confKeyBaseUrl]), + auth.WithMeta(auth.MetaKeyLogin, conf[confKeyDefaultLogin]), ) if err != nil { return err diff --git a/bridge/jira/jira.go b/bridge/jira/jira.go index b891ee3dccc7efe88e8a3a6d6bf83403b0caaa73..066c659775b8c890c9207afa96a16767de806e20 100644 --- a/bridge/jira/jira.go +++ b/bridge/jira/jira.go @@ -25,6 +25,7 @@ const ( confKeyBaseUrl = "base-url" confKeyProject = "project" + confKeyDefaultLogin = "default-login" confKeyCredentialType = "credentials-type" // "SESSION" or "TOKEN" confKeyIDMap = "bug-id-map" confKeyIDRevMap = "bug-id-revmap" diff --git a/bridge/launchpad/config.go b/bridge/launchpad/config.go index dfff0d3d0ff4747ac0217680eb8a0fcdd4bfa2fd..0bf8dc0d9cb9e667d7033f96bcaee3857663c21d 100644 --- a/bridge/launchpad/config.go +++ b/bridge/launchpad/config.go @@ -66,7 +66,6 @@ func (*Launchpad) ValidateConfig(conf core.Configuration) error { } else if v != target { return fmt.Errorf("unexpected target name: %v", v) } - if _, ok := conf[confKeyProject]; !ok { return fmt.Errorf("missing %s key", confKeyProject) } diff --git a/input/prompt.go b/input/prompt.go index 12aa7b926046d62b3e3eed80495b0edfc2d826ae..80f5a61481167e17581ef304a42fb8651db4af0a 100644 --- a/input/prompt.go +++ b/input/prompt.go @@ -48,10 +48,12 @@ func IsURL(name string, value string) (string, error) { // Prompts +// Prompt is a simple text input. func Prompt(prompt, name string, validators ...PromptValidator) (string, error) { return PromptDefault(prompt, name, "", validators...) } +// PromptDefault is a simple text input with a default value. func PromptDefault(prompt, name, preValue string, validators ...PromptValidator) (string, error) { loop: for { @@ -87,6 +89,7 @@ loop: } } +// PromptPassword is a specialized text input that doesn't display the characters entered. func PromptPassword(prompt, name string, validators ...PromptValidator) (string, error) { termState, err := terminal.GetState(syscall.Stdin) if err != nil { @@ -128,6 +131,8 @@ loop: } } +// PromptChoice is a prompt giving possible choices +// Return the index starting at zero of the choice selected. func PromptChoice(prompt string, choices []string) (int, error) { for { for i, choice := range choices {