From b92adfcb5f79f2b32c3dafb0fc3e7f1b753b6197 Mon Sep 17 00:00:00 2001 From: Michael Muré Date: Sun, 8 Dec 2019 21:15:06 +0100 Subject: bridge: huge refactor to accept multiple kind of credentials --- bridge/github/config.go | 135 ++++++++++++++++++++----------------------- bridge/github/config_test.go | 24 +++++--- bridge/github/export.go | 108 ++++++++++++++++++++-------------- bridge/github/export_test.go | 43 +++++++------- bridge/github/github.go | 5 +- bridge/github/import.go | 38 +++++++++--- bridge/github/import_test.go | 15 +++-- bridge/github/iterator.go | 4 +- 8 files changed, 214 insertions(+), 158 deletions(-) (limited to 'bridge/github') diff --git a/bridge/github/config.go b/bridge/github/config.go index 9d059b00..a74e7f6b 100644 --- a/bridge/github/config.go +++ b/bridge/github/config.go @@ -22,6 +22,8 @@ import ( "golang.org/x/crypto/ssh/terminal" "github.com/MichaelMure/git-bug/bridge/core" + "github.com/MichaelMure/git-bug/bridge/core/auth" + "github.com/MichaelMure/git-bug/cache" "github.com/MichaelMure/git-bug/entity" "github.com/MichaelMure/git-bug/repository" "github.com/MichaelMure/git-bug/util/colors" @@ -33,7 +35,6 @@ const ( githubV3Url = "https://api.github.com" keyOwner = "owner" keyProject = "project" - keyToken = "token" defaultTimeout = 60 * time.Second ) @@ -42,40 +43,33 @@ var ( ErrBadProjectURL = errors.New("bad project url") ) -func (g *Github) Configure(repo repository.RepoCommon, params core.BridgeParams) (core.Configuration, error) { +func (g *Github) Configure(repo *cache.RepoCache, params core.BridgeParams) (core.Configuration, error) { conf := make(core.Configuration) var err error - if (params.Token != "" || params.TokenId != "" || params.TokenStdin) && + if (params.CredPrefix != "" || params.TokenRaw != "") && (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") } var owner string var project string + // getting owner and project name switch { case params.Owner != "" && params.Project != "": // first try to use params if both or project and owner are provided owner = params.Owner project = params.Project - case params.URL != "": // try to parse params URL and extract owner and project owner, project, err = splitURL(params.URL) if err != nil { return nil, err } - default: - // remote suggestions - remotes, err := repo.GetRemotes() - if err != nil { - return nil, err - } - // terminal prompt - owner, project, err = promptURL(remotes) + owner, project, err = promptURL(repo) if err != nil { return nil, err } @@ -90,49 +84,38 @@ func (g *Github) Configure(repo repository.RepoCommon, params core.BridgeParams) return nil, fmt.Errorf("invalid parameter owner: %v", owner) } - var token string - var tokenId entity.Id - var tokenObj *core.Token - - // 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') - if err != nil { - return nil, fmt.Errorf("reading from stdin: %v", err) - } - token = strings.TrimSpace(token) - } else if params.TokenId != "" { - tokenId = entity.Id(params.TokenId) - } else { - tokenObj, err = promptTokenOptions(repo, owner, project) - if err != nil { - return nil, err - } + user, err := repo.GetUserIdentity() + 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 = core.LoadOrCreateToken(repo, target, token) + var cred auth.Credential + + switch { + case params.CredPrefix != "": + cred, err = auth.LoadWithPrefix(repo, params.CredPrefix) if err != nil { return nil, err } - } else if tokenId != "" { - tokenObj, err = core.LoadToken(repo, tokenId) + if cred.UserId() != user.Id() { + return nil, fmt.Errorf("selected credential don't match the user") + } + case params.TokenRaw != "": + cred = auth.NewToken(user.Id(), params.TokenRaw, target) + default: + cred, err = promptTokenOptions(repo, user.Id(), owner, project) if err != nil { return nil, err } - if tokenObj.Target != target { - return nil, fmt.Errorf("token target is incompatible %s", tokenObj.Target) - } + } + + token, ok := cred.(*auth.Token) + if !ok { + return nil, fmt.Errorf("the Github bridge only handle token credentials") } // verify access to the repository with token - ok, err = validateProject(owner, project, tokenObj.Value) + ok, err = validateProject(owner, project, token) if err != nil { return nil, err } @@ -141,7 +124,6 @@ func (g *Github) Configure(repo repository.RepoCommon, params core.BridgeParams) } conf[core.ConfigKeyTarget] = target - conf[core.ConfigKeyTokenId] = tokenObj.ID().String() conf[keyOwner] = owner conf[keyProject] = project @@ -150,6 +132,14 @@ func (g *Github) Configure(repo repository.RepoCommon, params core.BridgeParams) return nil, err } + // don't forget to store the now known valid token + if !auth.IdExist(repo, cred.ID()) { + err = auth.Store(repo, cred) + if err != nil { + return nil, err + } + } + return conf, nil } @@ -160,10 +150,6 @@ func (*Github) ValidateConfig(conf core.Configuration) error { return fmt.Errorf("unexpected target name: %v", v) } - if _, ok := conf[core.ConfigKeyTokenId]; !ok { - return fmt.Errorf("missing %s key", core.ConfigKeyTokenId) - } - if _, ok := conf[keyOwner]; !ok { return fmt.Errorf("missing %s key", keyOwner) } @@ -245,9 +231,9 @@ func randomFingerprint() string { return string(b) } -func promptTokenOptions(repo repository.RepoCommon, owner, project string) (*core.Token, error) { +func promptTokenOptions(repo repository.RepoConfig, userId entity.Id, owner, project string) (auth.Credential, error) { for { - tokens, err := core.LoadTokensWithTarget(repo, target) + creds, err := auth.List(repo, auth.WithUserId(userId), auth.WithTarget(target)) if err != nil { return nil, err } @@ -256,18 +242,19 @@ func promptTokenOptions(repo repository.RepoCommon, owner, project string) (*cor fmt.Println("[1]: enter my token") fmt.Println("[2]: interactive token creation") - if len(tokens) > 0 { + if len(creds) > 0 { + sort.Sort(auth.ById(creds)) + fmt.Println() fmt.Println("Existing tokens for Github:") - for i, token := range tokens { - if token.Target == target { - fmt.Printf("[%d]: %s => %s (%s)\n", - i+3, - colors.Cyan(token.ID().Human()), - text.TruncateMax(token.Value, 10), - token.CreateTime.Format(time.RFC822), - ) - } + for i, cred := range creds { + token := cred.(*auth.Token) + fmt.Printf("[%d]: %s => %s (%s)\n", + i+3, + colors.Cyan(token.ID().Human()), + colors.Red(text.TruncateMax(token.Value, 10)), + token.CreateTime().Format(time.RFC822), + ) } } @@ -281,30 +268,28 @@ func promptTokenOptions(repo repository.RepoCommon, owner, project string) (*cor } line = strings.TrimSpace(line) - index, err := strconv.Atoi(line) - if err != nil || index < 1 || index > len(tokens)+2 { + if err != nil || index < 1 || index > len(creds)+2 { fmt.Println("invalid input") continue } - var token string switch index { case 1: - token, err = promptToken() + value, err := promptToken() if err != nil { return nil, err } + return auth.NewToken(userId, value, target), nil case 2: - token, err = loginAndRequestToken(owner, project) + value, err := loginAndRequestToken(owner, project) if err != nil { return nil, err } + return auth.NewToken(userId, value, target), nil default: - return tokens[index-3], nil + return creds[index-3], nil } - - return core.LoadOrCreateToken(repo, target, token) } } @@ -435,7 +420,13 @@ func promptUsername() (string, error) { } } -func promptURL(remotes map[string]string) (string, string, error) { +func promptURL(repo repository.RepoCommon) (string, string, error) { + // remote suggestions + remotes, err := repo.GetRemotes() + if err != nil { + return "", "", err + } + validRemotes := getValidGithubRemoteURLs(remotes) if len(validRemotes) > 0 { for { @@ -556,7 +547,7 @@ func validateUsername(username string) (bool, error) { return resp.StatusCode == http.StatusOK, nil } -func validateProject(owner, project, token string) (bool, error) { +func validateProject(owner, project string, token *auth.Token) (bool, error) { url := fmt.Sprintf("%s/repos/%s/%s", githubV3Url, owner, project) req, err := http.NewRequest("GET", url, nil) @@ -565,7 +556,7 @@ func validateProject(owner, project, token string) (bool, error) { } // need the token for private repositories - req.Header.Set("Authorization", fmt.Sprintf("token %s", token)) + req.Header.Set("Authorization", fmt.Sprintf("token %s", token.Value)) client := &http.Client{ Timeout: defaultTimeout, diff --git a/bridge/github/config_test.go b/bridge/github/config_test.go index 4feeaa74..9798d26b 100644 --- a/bridge/github/config_test.go +++ b/bridge/github/config_test.go @@ -5,6 +5,9 @@ import ( "testing" "github.com/stretchr/testify/assert" + + "github.com/MichaelMure/git-bug/bridge/core/auth" + "github.com/MichaelMure/git-bug/entity" ) func TestSplitURL(t *testing.T) { @@ -142,20 +145,23 @@ func TestValidateUsername(t *testing.T) { } func TestValidateProject(t *testing.T) { - tokenPrivateScope := os.Getenv("GITHUB_TOKEN_PRIVATE") - if tokenPrivateScope == "" { + envPrivate := os.Getenv("GITHUB_TOKEN_PRIVATE") + if envPrivate == "" { t.Skip("Env var GITHUB_TOKEN_PRIVATE missing") } - tokenPublicScope := os.Getenv("GITHUB_TOKEN_PUBLIC") - if tokenPublicScope == "" { + envPublic := os.Getenv("GITHUB_TOKEN_PUBLIC") + if envPublic == "" { t.Skip("Env var GITHUB_TOKEN_PUBLIC missing") } + tokenPrivate := auth.NewToken(entity.UnsetId, envPrivate, target) + tokenPublic := auth.NewToken(entity.UnsetId, envPublic, target) + type args struct { owner string project string - token string + token *auth.Token } tests := []struct { name string @@ -167,7 +173,7 @@ func TestValidateProject(t *testing.T) { args: args{ project: "git-bug", owner: "MichaelMure", - token: tokenPublicScope, + token: tokenPublic, }, want: true, }, @@ -176,7 +182,7 @@ func TestValidateProject(t *testing.T) { args: args{ project: "git-bug-test-github-bridge", owner: "MichaelMure", - token: tokenPrivateScope, + token: tokenPrivate, }, want: true, }, @@ -185,7 +191,7 @@ func TestValidateProject(t *testing.T) { args: args{ project: "git-bug-test-github-bridge", owner: "MichaelMure", - token: tokenPublicScope, + token: tokenPublic, }, want: false, }, @@ -194,7 +200,7 @@ func TestValidateProject(t *testing.T) { args: args{ project: "cant-find-this", owner: "organisation-not-found", - token: tokenPublicScope, + token: tokenPublic, }, want: false, }, diff --git a/bridge/github/export.go b/bridge/github/export.go index 8d515802..6c089a47 100644 --- a/bridge/github/export.go +++ b/bridge/github/export.go @@ -15,9 +15,11 @@ import ( "golang.org/x/sync/errgroup" "github.com/MichaelMure/git-bug/bridge/core" + "github.com/MichaelMure/git-bug/bridge/core/auth" "github.com/MichaelMure/git-bug/bug" "github.com/MichaelMure/git-bug/cache" "github.com/MichaelMure/git-bug/entity" + "github.com/MichaelMure/git-bug/repository" ) var ( @@ -31,8 +33,12 @@ type githubExporter struct { // cache identities clients identityClient map[entity.Id]*githubv4.Client - // map identities with their tokens - identityToken map[entity.Id]string + // the client to use for non user-specific queries + // should be the client of the default user + defaultClient *githubv4.Client + + // the token of the default user + defaultToken *auth.Token // github repository ID repositoryID string @@ -46,68 +52,86 @@ type githubExporter struct { } // Init . -func (ge *githubExporter) Init(conf core.Configuration) error { +func (ge *githubExporter) Init(repo *cache.RepoCache, conf core.Configuration) error { ge.conf = conf - //TODO: initialize with multiple tokens - ge.identityToken = make(map[entity.Id]string) ge.identityClient = make(map[entity.Id]*githubv4.Client) 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) + if err != nil { + return err + } + + ge.defaultClient, err = ge.getClientForIdentity(user.Id()) + if err != nil { + return err + } + + creds, err := auth.List(repo, auth.WithUserId(user.Id()), 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 } -// getIdentityClient return a githubv4 API client configured with the access token of the given identity. -// if no client were found it will initialize it from the known tokens map and cache it for next use -func (ge *githubExporter) getIdentityClient(id entity.Id) (*githubv4.Client, error) { - client, ok := ge.identityClient[id] - if ok { - return client, nil +func (ge *githubExporter) cacheAllClient(repo repository.RepoConfig) error { + creds, err := auth.List(repo, auth.WithTarget(target), auth.WithKind(auth.KindToken)) + if err != nil { + return err } - // get token - token, ok := ge.identityToken[id] - if !ok { - return nil, ErrMissingIdentityToken + for _, cred := range creds { + if _, ok := ge.identityClient[cred.UserId()]; !ok { + client := buildClient(creds[0].(*auth.Token)) + ge.identityClient[cred.UserId()] = client + } } - // create client - client = buildClient(token) - // cache client - ge.identityClient[id] = client + return nil +} - return client, nil +// getClientForIdentity return a githubv4 API client configured with the access token of the given identity. +func (ge *githubExporter) getClientForIdentity(userId entity.Id) (*githubv4.Client, error) { + client, ok := ge.identityClient[userId] + if ok { + return client, nil + } + + return nil, ErrMissingIdentityToken } // ExportAll export all event made by the current user to Github func (ge *githubExporter) ExportAll(ctx context.Context, repo *cache.RepoCache, since time.Time) (<-chan core.ExportResult, error) { out := make(chan core.ExportResult) - user, err := repo.GetUserIdentity() - if err != nil { - return nil, err - } - - ge.identityToken[user.Id()] = ge.conf[core.ConfigKeyToken] - + var err error // get repository node id ge.repositoryID, err = getRepositoryNodeID( ctx, + ge.defaultToken, ge.conf[keyOwner], ge.conf[keyProject], - ge.conf[core.ConfigKeyToken], ) - - if err != nil { - return nil, err - } - - client, err := ge.getIdentityClient(user.Id()) if err != nil { return nil, err } // query all labels - err = ge.cacheGithubLabels(ctx, client) + err = ge.cacheGithubLabels(ctx, ge.defaultClient) if err != nil { return nil, err } @@ -115,8 +139,8 @@ func (ge *githubExporter) ExportAll(ctx context.Context, repo *cache.RepoCache, go func() { defer close(out) - var allIdentitiesIds []entity.Id - for id := range ge.identityToken { + allIdentitiesIds := make([]entity.Id, 0, len(ge.identityClient)) + for id := range ge.identityClient { allIdentitiesIds = append(allIdentitiesIds, id) } @@ -209,7 +233,7 @@ func (ge *githubExporter) exportBug(ctx context.Context, b *cache.BugCache, sinc } else { // check that we have a token for operation author - client, err := ge.getIdentityClient(author.Id()) + client, err := ge.getClientForIdentity(author.Id()) if err != nil { // if bug is still not exported and we do not have the author stop the execution out <- core.NewExportNothing(b.Id(), fmt.Sprintf("missing author token")) @@ -262,7 +286,7 @@ func (ge *githubExporter) exportBug(ctx context.Context, b *cache.BugCache, sinc } opAuthor := op.GetAuthor() - client, err := ge.getIdentityClient(opAuthor.Id()) + client, err := ge.getClientForIdentity(opAuthor.Id()) if err != nil { continue } @@ -384,7 +408,7 @@ func (ge *githubExporter) exportBug(ctx context.Context, b *cache.BugCache, sinc } // getRepositoryNodeID request github api v3 to get repository node id -func getRepositoryNodeID(ctx context.Context, owner, project, token string) (string, error) { +func getRepositoryNodeID(ctx context.Context, token *auth.Token, owner, project string) (string, error) { url := fmt.Sprintf("%s/repos/%s/%s", githubV3Url, owner, project) client := &http.Client{} @@ -394,7 +418,7 @@ func getRepositoryNodeID(ctx context.Context, owner, project, token string) (str } // need the token for private repositories - req.Header.Set("Authorization", fmt.Sprintf("token %s", token)) + req.Header.Set("Authorization", fmt.Sprintf("token %s", token.Value)) ctx, cancel := context.WithTimeout(ctx, defaultTimeout) defer cancel() @@ -512,7 +536,7 @@ func (ge *githubExporter) createGithubLabel(ctx context.Context, label, color st req = req.WithContext(ctx) // need the token for private repositories - req.Header.Set("Authorization", fmt.Sprintf("token %s", ge.conf[core.ConfigKeyToken])) + req.Header.Set("Authorization", fmt.Sprintf("token %s", ge.defaultToken.Value)) resp, err := client.Do(req) if err != nil { diff --git a/bridge/github/export_test.go b/bridge/github/export_test.go index dba72f3f..5a0bc653 100644 --- a/bridge/github/export_test.go +++ b/bridge/github/export_test.go @@ -14,6 +14,7 @@ import ( "github.com/stretchr/testify/require" "github.com/MichaelMure/git-bug/bridge/core" + "github.com/MichaelMure/git-bug/bridge/core/auth" "github.com/MichaelMure/git-bug/bug" "github.com/MichaelMure/git-bug/cache" "github.com/MichaelMure/git-bug/repository" @@ -30,7 +31,7 @@ type testCase struct { numOrOp int // number of original operations } -func testCases(t *testing.T, repo *cache.RepoCache, identity *cache.IdentityCache) []*testCase { +func testCases(t *testing.T, repo *cache.RepoCache) []*testCase { // simple bug simpleBug, _, err := repo.NewBug("simple bug", "new bug") require.NoError(t, err) @@ -92,32 +93,32 @@ func testCases(t *testing.T, repo *cache.RepoCache, identity *cache.IdentityCach require.NoError(t, err) return []*testCase{ - &testCase{ + { name: "simple bug", bug: simpleBug, numOrOp: 1, }, - &testCase{ + { name: "bug with comments", bug: bugWithComments, numOrOp: 2, }, - &testCase{ + { name: "bug label change", bug: bugLabelChange, numOrOp: 6, }, - &testCase{ + { name: "bug with comment editions", bug: bugWithCommentEditions, numOrOp: 4, }, - &testCase{ + { name: "bug changed status", bug: bugStatusChanged, numOrOp: 3, }, - &testCase{ + { name: "bug title edited", bug: bugTitleEdited, numOrOp: 2, @@ -127,11 +128,11 @@ func testCases(t *testing.T, repo *cache.RepoCache, identity *cache.IdentityCach func TestPushPull(t *testing.T) { // repo owner - user := os.Getenv("GITHUB_TEST_USER") + envUser := os.Getenv("GITHUB_TEST_USER") // token must have 'repo' and 'delete_repo' scopes - token := os.Getenv("GITHUB_TOKEN_ADMIN") - if token == "" { + envToken := os.Getenv("GITHUB_TOKEN_ADMIN") + if envToken == "" { t.Skip("Env var GITHUB_TOKEN_ADMIN missing") } @@ -152,35 +153,38 @@ func TestPushPull(t *testing.T) { defer backend.Close() interrupt.RegisterCleaner(backend.Close) - tests := testCases(t, backend, author) + tests := testCases(t, backend) // generate project name projectName := generateRepoName() // create target Github repository - err = createRepository(projectName, token) + err = createRepository(projectName, envToken) require.NoError(t, err) fmt.Println("created repository", projectName) // Make sure to remove the Github repository when the test end defer func(t *testing.T) { - if err := deleteRepository(projectName, user, token); err != nil { + if err := deleteRepository(projectName, envUser, envToken); err != nil { t.Fatal(err) } fmt.Println("deleted repository:", projectName) }(t) interrupt.RegisterCleaner(func() error { - return deleteRepository(projectName, user, token) + return deleteRepository(projectName, envUser, envToken) }) + token := auth.NewToken(author.Id(), envToken, target) + err = auth.Store(repo, token) + require.NoError(t, err) + // initialize exporter exporter := &githubExporter{} - err = exporter.Init(core.Configuration{ - keyOwner: user, + err = exporter.Init(backend, core.Configuration{ + keyOwner: envUser, keyProject: projectName, - keyToken: token, }) require.NoError(t, err) @@ -206,10 +210,9 @@ func TestPushPull(t *testing.T) { require.NoError(t, err) importer := &githubImporter{} - err = importer.Init(core.Configuration{ - keyOwner: user, + err = importer.Init(backend, core.Configuration{ + keyOwner: envUser, keyProject: projectName, - keyToken: token, }) require.NoError(t, err) diff --git a/bridge/github/github.go b/bridge/github/github.go index e4fb03dd..874c2d11 100644 --- a/bridge/github/github.go +++ b/bridge/github/github.go @@ -8,6 +8,7 @@ import ( "golang.org/x/oauth2" "github.com/MichaelMure/git-bug/bridge/core" + "github.com/MichaelMure/git-bug/bridge/core/auth" ) type Github struct{} @@ -24,9 +25,9 @@ func (*Github) NewExporter() core.Exporter { return &githubExporter{} } -func buildClient(token string) *githubv4.Client { +func buildClient(token *auth.Token) *githubv4.Client { src := oauth2.StaticTokenSource( - &oauth2.Token{AccessToken: token}, + &oauth2.Token{AccessToken: token.Value}, ) httpClient := oauth2.NewClient(context.TODO(), src) diff --git a/bridge/github/import.go b/bridge/github/import.go index c0fb3d6c..1421dd96 100644 --- a/bridge/github/import.go +++ b/bridge/github/import.go @@ -8,6 +8,7 @@ import ( "github.com/shurcooL/githubv4" "github.com/MichaelMure/git-bug/bridge/core" + "github.com/MichaelMure/git-bug/bridge/core/auth" "github.com/MichaelMure/git-bug/bug" "github.com/MichaelMure/git-bug/cache" "github.com/MichaelMure/git-bug/entity" @@ -24,6 +25,9 @@ const ( type githubImporter struct { conf core.Configuration + // default user client + client *githubv4.Client + // iterator iterator *iterator @@ -31,15 +35,37 @@ type githubImporter struct { out chan<- core.ImportResult } -func (gi *githubImporter) Init(conf core.Configuration) error { +func (gi *githubImporter) Init(repo *cache.RepoCache, conf core.Configuration) error { gi.conf = conf + + opts := []auth.Option{ + auth.WithTarget(target), + auth.WithKind(auth.KindToken), + } + + user, err := repo.GetUserIdentity() + if err == nil { + opts = append(opts, auth.WithUserId(user.Id())) + } + + creds, err := auth.List(repo, opts...) + if err != nil { + return err + } + + if len(creds) == 0 { + return ErrMissingIdentityToken + } + + gi.client = buildClient(creds[0].(*auth.Token)) + return nil } // ImportAll iterate over all the configured repository issues and ensure the creation of the // missing issues / timeline items / edits / label events ... func (gi *githubImporter) ImportAll(ctx context.Context, repo *cache.RepoCache, since time.Time) (<-chan core.ImportResult, error) { - gi.iterator = NewIterator(ctx, 10, gi.conf[keyOwner], gi.conf[keyProject], gi.conf[core.ConfigKeyToken], since) + gi.iterator = NewIterator(ctx, gi.client, 10, gi.conf[keyOwner], gi.conf[keyProject], since) out := make(chan core.ImportResult) gi.out = out @@ -494,7 +520,7 @@ func (gi *githubImporter) ensurePerson(repo *cache.RepoCache, actor *actor) (*ca if err == nil { return i, nil } - if _, ok := err.(entity.ErrMultipleMatch); ok { + if entity.IsErrMultipleMatch(err) { return nil, err } @@ -543,7 +569,7 @@ func (gi *githubImporter) getGhost(repo *cache.RepoCache) (*cache.IdentityCache, if err == nil { return i, nil } - if _, ok := err.(entity.ErrMultipleMatch); ok { + if entity.IsErrMultipleMatch(err) { return nil, err } @@ -553,12 +579,10 @@ func (gi *githubImporter) getGhost(repo *cache.RepoCache) (*cache.IdentityCache, "login": githubv4.String("ghost"), } - gc := buildClient(gi.conf[core.ConfigKeyToken]) - ctx, cancel := context.WithTimeout(gi.iterator.ctx, defaultTimeout) defer cancel() - err = gc.Query(ctx, &q, variables) + err = gi.client.Query(ctx, &q, variables) if err != nil { return nil, err } diff --git a/bridge/github/import_test.go b/bridge/github/import_test.go index f1558831..304229a0 100644 --- a/bridge/github/import_test.go +++ b/bridge/github/import_test.go @@ -11,6 +11,7 @@ import ( "github.com/stretchr/testify/require" "github.com/MichaelMure/git-bug/bridge/core" + "github.com/MichaelMure/git-bug/bridge/core/auth" "github.com/MichaelMure/git-bug/bug" "github.com/MichaelMure/git-bug/cache" "github.com/MichaelMure/git-bug/identity" @@ -134,16 +135,22 @@ func Test_Importer(t *testing.T) { defer backend.Close() interrupt.RegisterCleaner(backend.Close) - token := os.Getenv("GITHUB_TOKEN_PRIVATE") - if token == "" { + envToken := os.Getenv("GITHUB_TOKEN_PRIVATE") + if envToken == "" { t.Skip("Env var GITHUB_TOKEN_PRIVATE missing") } + err = author.Commit(repo) + require.NoError(t, err) + + token := auth.NewToken(author.Id(), envToken, target) + err = auth.Store(repo, token) + require.NoError(t, err) + importer := &githubImporter{} - err = importer.Init(core.Configuration{ + err = importer.Init(backend, core.Configuration{ keyOwner: "MichaelMure", keyProject: "git-bug-test-github-bridge", - keyToken: token, }) require.NoError(t, err) diff --git a/bridge/github/iterator.go b/bridge/github/iterator.go index d1d7900f..40b00292 100644 --- a/bridge/github/iterator.go +++ b/bridge/github/iterator.go @@ -63,9 +63,9 @@ type iterator struct { } // NewIterator create and initialize a new iterator -func NewIterator(ctx context.Context, capacity int, owner, project, token string, since time.Time) *iterator { +func NewIterator(ctx context.Context, client *githubv4.Client, capacity int, owner, project string, since time.Time) *iterator { i := &iterator{ - gc: buildClient(token), + gc: client, since: since, capacity: capacity, ctx: ctx, -- cgit