import.go

  1package gitea
  2
  3import (
  4	"context"
  5	"fmt"
  6	"strconv"
  7	"time"
  8
  9	"code.gitea.io/sdk/gitea"
 10
 11	"github.com/MichaelMure/git-bug/bridge/core"
 12	"github.com/MichaelMure/git-bug/bridge/core/auth"
 13	"github.com/MichaelMure/git-bug/bridge/gitea/iterator"
 14	"github.com/MichaelMure/git-bug/cache"
 15	"github.com/MichaelMure/git-bug/entities/bug"
 16	"github.com/MichaelMure/git-bug/entity"
 17	"github.com/MichaelMure/git-bug/util/text"
 18)
 19
 20// giteaImporter implement the Importer interface
 21type giteaImporter struct {
 22	conf core.Configuration
 23
 24	// default client
 25	client *gitea.Client
 26
 27	// iterator
 28	iterator *iterator.Iterator
 29
 30	// send only channel
 31	out chan<- core.ImportResult
 32}
 33
 34func (gi *giteaImporter) Init(_ context.Context, repo *cache.RepoCache, conf core.Configuration) error {
 35	gi.conf = conf
 36
 37	creds, err := auth.List(repo,
 38		auth.WithTarget(target),
 39		auth.WithKind(auth.KindToken),
 40		auth.WithMeta(auth.MetaKeyBaseURL, conf[confKeyBaseURL]),
 41		auth.WithMeta(auth.MetaKeyLogin, conf[confKeyDefaultLogin]),
 42	)
 43	if err != nil {
 44		return err
 45	}
 46
 47	if len(creds) == 0 {
 48		return ErrMissingIdentityToken
 49	}
 50
 51	gi.client, err = buildClient(conf[confKeyBaseURL], creds[0].(*auth.Token))
 52	if err != nil {
 53		return err
 54	}
 55
 56	return nil
 57}
 58
 59// ImportAll iterate over all the configured repository issues (comments) and ensure the creation
 60// of the missing issues / comments / label events / title changes ...
 61func (gi *giteaImporter) ImportAll(ctx context.Context, repo *cache.RepoCache, since time.Time) (<-chan core.ImportResult, error) {
 62	gi.iterator = iterator.NewIterator(ctx, gi.client, 10, gi.conf[confKeyOwner], gi.conf[confKeyProject], defaultTimeout, since)
 63	out := make(chan core.ImportResult)
 64	gi.out = out
 65
 66	go func() {
 67		defer close(gi.out)
 68
 69		// Loop over all matching issues
 70		for gi.iterator.NextIssue() {
 71			issue := gi.iterator.IssueValue()
 72
 73			// create issue
 74			b, err := gi.ensureIssue(repo, issue)
 75			if err != nil {
 76				err := fmt.Errorf("issue creation: %v", err)
 77				out <- core.NewImportError(err, "")
 78				return
 79			}
 80
 81			// Loop over all comments
 82
 83			// Loop over all label events
 84
 85			if !b.NeedCommit() {
 86				out <- core.NewImportNothing(b.Id(), "no imported operation")
 87			} else if err := b.Commit(); err != nil {
 88				// commit bug state
 89				err := fmt.Errorf("bug commit: %v", err)
 90				out <- core.NewImportError(err, "")
 91				return
 92			}
 93		}
 94
 95		if err := gi.iterator.Error(); err != nil {
 96			out <- core.NewImportError(err, "")
 97		}
 98	}()
 99
100	return out, nil
101}
102
103func (gi *giteaImporter) ensureIssue(repo *cache.RepoCache, issue *gitea.Issue) (*cache.BugCache, error) {
104	// ensure issue author
105	author, err := gi.ensurePerson(repo, issue.Poster.UserName)
106	if err != nil {
107		return nil, err
108	}
109
110	giteaID := strconv.FormatInt(issue.Index, 10)
111
112	// resolve bug
113	b, err := repo.ResolveBugMatcher(func(excerpt *cache.BugExcerpt) bool {
114		return excerpt.CreateMetadata[core.MetaKeyOrigin] == target &&
115			excerpt.CreateMetadata[metaKeyGiteaID] == giteaID &&
116			excerpt.CreateMetadata[metaKeyGiteaBaseURL] == gi.conf[confKeyBaseURL] &&
117			excerpt.CreateMetadata[metaKeyGiteaOwner] == gi.conf[confKeyOwner] &&
118			excerpt.CreateMetadata[metaKeyGiteaProject] == gi.conf[confKeyProject]
119	})
120	if err == nil {
121		return b, nil
122	}
123	if err != bug.ErrBugNotExist {
124		return nil, err
125	}
126
127	// if bug was never imported, create bug
128	b, _, err = repo.NewBugRaw(
129		author,
130		issue.Created.Unix(),
131		text.CleanupOneLine(issue.Title),
132		text.Cleanup(issue.Body),
133		nil,
134		map[string]string{
135			core.MetaKeyOrigin:  target,
136			metaKeyGiteaID:      giteaID,
137			metaKeyGiteaOwner:   gi.conf[confKeyOwner],
138			metaKeyGiteaProject: gi.conf[confKeyProject],
139			metaKeyGiteaBaseURL: gi.conf[confKeyBaseURL],
140		},
141	)
142
143	if err != nil {
144		return nil, err
145	}
146
147	// importing a new bug
148	gi.out <- core.NewImportBug(b.Id())
149
150	return b, nil
151}
152
153func (gi *giteaImporter) ensurePerson(repo *cache.RepoCache, loginName string) (*cache.IdentityCache, error) {
154	// Look first in the cache
155	i, err := repo.ResolveIdentityImmutableMetadata(metaKeyGiteaLogin, loginName)
156	if err == nil {
157		return i, nil
158	}
159	if entity.IsErrMultipleMatch(err) {
160		return nil, err
161	}
162
163	ctx, cancel := context.WithTimeout(context.Background(), defaultTimeout)
164	defer cancel()
165	gi.client.SetContext(ctx)
166
167	user, _, err := gi.client.GetUserInfo(loginName)
168	if err != nil {
169		return nil, err
170	}
171
172	i, err = repo.NewIdentityRaw(
173		user.FullName,
174		user.Email,
175		user.UserName,
176		user.AvatarURL,
177		nil,
178		map[string]string{
179			// because Gitea
180			metaKeyGiteaLogin: user.UserName,
181		},
182	)
183	if err != nil {
184		return nil, err
185	}
186
187	gi.out <- core.NewImportIdentity(i.Id())
188	return i, nil
189}