[bug] add snapshot.GetCreateMetadata method

Amine Hilaly created

[bug] add snapshot.HasParticipant(id string)
[bug] add snapshot.HasAnyParticipant(ids ...string)
[bug] add snapshot.HasActor(id string)
[bug] add snapshot.HasAnyActor(ids ...string)
[bridge/github] improve comments
[bridge/github] exporter tests: register deleteRepository in cleaner
[bridge/github] tests rebase

Change summary

bridge/github/export.go          | 67 +++++++++++++++++----------------
bridge/github/export_mutation.go |  4 +-
bridge/github/export_test.go     | 26 +++++++-----
bug/snapshot.go                  | 45 ++++++++++++++++++++++
4 files changed, 96 insertions(+), 46 deletions(-)

Detailed changes

bridge/github/export.go 🔗

@@ -122,8 +122,13 @@ func (ge *githubExporter) ExportAll(repo *cache.RepoCache, since time.Time) erro
 		return err
 	}
 
+	allIdentitiesIds := []string{}
+	for id := range ge.identityToken {
+		allIdentitiesIds = append(allIdentitiesIds, id)
+	}
+
 	allBugsIds := repo.AllBugsIds()
-bugLoop:
+
 	for _, id := range allBugsIds {
 		b, err := repo.ResolveBug(id)
 		if err != nil {
@@ -137,20 +142,13 @@ bugLoop:
 			continue
 		}
 
-		for _, p := range snapshot.Participants {
-			// if we have a token for one of the participants
-			for userId := range ge.identityToken {
-				if p.Id() == userId {
-					// try to export the bug and it associated events
-					if err := ge.exportBug(b, since); err != nil {
-						return err
-					}
-
-					// avoid calling exportBug multiple times for the same bug
-					continue bugLoop
-				}
+		if snapshot.HasAnyParticipant(allIdentitiesIds...) {
+			// try to export the bug and it associated events
+			if err := ge.exportBug(b, since); err != nil {
+				return err
 			}
 		}
+
 	}
 
 	fmt.Printf("Successfully exported %d issues and %d labels to Github\n", ge.exportedBugs, ge.exportedLabels)
@@ -171,7 +169,7 @@ func (ge *githubExporter) exportBug(b *cache.BugCache, since time.Time) error {
 
 	// first operation is always createOp
 	createOp := snapshot.Operations[0].(*bug.CreateOperation)
-	author := createOp.GetAuthor()
+	author := snapshot.Author
 
 	// skip bug if origin is not allowed
 	origin, ok := createOp.GetMetadata(keyOrigin)
@@ -440,6 +438,9 @@ func (ge *githubExporter) getGithubLabelID(gc *githubv4.Client, label string) (s
 	return q.Repository.Label.ID, nil
 }
 
+// create a new label and return it github id
+// NOTE: since createLabel mutation is still in preview mode we use github api v3 to create labels
+// see https://developer.github.com/v4/mutation/createlabel/ and https://developer.github.com/v4/previews/#labels-preview
 func (ge *githubExporter) createGithubLabel(label, color string) (string, error) {
 	url := fmt.Sprintf("%s/repos/%s/%s/labels", githubV3Url, ge.conf[keyOwner], ge.conf[keyProject])
 
@@ -495,6 +496,7 @@ func (ge *githubExporter) createGithubLabel(label, color string) (string, error)
 	return aux.NodeID, nil
 }
 
+/**
 // create github label using api v4
 func (ge *githubExporter) createGithubLabelV4(gc *githubv4.Client, label, labelColor string) (string, error) {
 	m := createLabelMutation{}
@@ -514,6 +516,7 @@ func (ge *githubExporter) createGithubLabelV4(gc *githubv4.Client, label, labelC
 
 	return m.CreateLabel.Label.ID, nil
 }
+*/
 
 func (ge *githubExporter) getOrCreateGithubLabelID(gc *githubv4.Client, repositoryID string, label bug.Label) (string, error) {
 	// try to get label id
@@ -526,9 +529,6 @@ func (ge *githubExporter) getOrCreateGithubLabelID(gc *githubv4.Client, reposito
 	rgba := label.RGBA()
 	hexColor := fmt.Sprintf("%.2x%.2x%.2x", rgba.R, rgba.G, rgba.B)
 
-	// create label and return id
-	// NOTE: since createLabel mutation is still in preview mode we use github api v4 to create labels
-	// see https://developer.github.com/v4/mutation/createlabel/ and https://developer.github.com/v4/previews/#labels-preview
 	labelID, err = ge.createGithubLabel(string(label), hexColor)
 	if err != nil {
 		return "", err
@@ -705,26 +705,27 @@ func (ge *githubExporter) updateGithubIssueLabels(gc *githubv4.Client, labelable
 	}
 	cancel()
 
-	if len(removed) > 0 {
-		removedIDs, err := ge.getLabelsIDs(gc, labelableID, removed)
-		if err != nil {
-			return errors.Wrap(err, "getting added labels ids")
-		}
+	if len(removed) == 0 {
+		return nil
+	}
 
-		m2 := &removeLabelsFromLabelableMutation{}
-		inputRemove := githubv4.RemoveLabelsFromLabelableInput{
-			LabelableID: labelableID,
-			LabelIDs:    removedIDs,
-		}
+	removedIDs, err := ge.getLabelsIDs(gc, labelableID, removed)
+	if err != nil {
+		return errors.Wrap(err, "getting added labels ids")
+	}
 
-		ctx, cancel := context.WithTimeout(parentCtx, defaultTimeout)
-		defer cancel()
+	m2 := &removeLabelsFromLabelableMutation{}
+	inputRemove := githubv4.RemoveLabelsFromLabelableInput{
+		LabelableID: labelableID,
+		LabelIDs:    removedIDs,
+	}
 
-		// remove label labels
-		if err := gc.Mutate(ctx, m2, inputRemove, nil); err != nil {
-			return err
-		}
+	ctx, cancel = context.WithTimeout(parentCtx, defaultTimeout)
+	defer cancel()
 
+	// remove label labels
+	if err := gc.Mutate(ctx, m2, inputRemove, nil); err != nil {
+		return err
 	}
 
 	return nil

bridge/github/export_mutation.go 🔗

@@ -1,7 +1,5 @@
 package github
 
-import "github.com/shurcooL/githubv4"
-
 type createIssueMutation struct {
 	CreateIssue struct {
 		Issue struct {
@@ -56,6 +54,7 @@ type addLabelsToLabelableMutation struct {
 	} `graphql:"addLabelsToLabelable(input:$input)"`
 }
 
+/**
 type createLabelMutation struct {
 	CreateLabel struct {
 		Label struct {
@@ -72,3 +71,4 @@ type createLabelInput struct {
 
 	ClientMutationID *githubv4.String `json:"clientMutationId,omitempty"`
 }
+*/

bridge/github/export_test.go 🔗

@@ -31,13 +31,13 @@ type testCase struct {
 
 func testCases(repo *cache.RepoCache, identity *cache.IdentityCache) ([]*testCase, error) {
 	// simple bug
-	simpleBug, err := repo.NewBug("simple bug", "new bug")
+	simpleBug, _, err := repo.NewBug("simple bug", "new bug")
 	if err != nil {
 		return nil, err
 	}
 
 	// bug with comments
-	bugWithComments, err := repo.NewBug("bug with comments", "new bug")
+	bugWithComments, _, err := repo.NewBug("bug with comments", "new bug")
 	if err != nil {
 		return nil, err
 	}
@@ -48,7 +48,7 @@ func testCases(repo *cache.RepoCache, identity *cache.IdentityCache) ([]*testCas
 	}
 
 	// bug with label changes
-	bugLabelChange, err := repo.NewBug("bug label change", "new bug")
+	bugLabelChange, _, err := repo.NewBug("bug label change", "new bug")
 	if err != nil {
 		return nil, err
 	}
@@ -69,12 +69,12 @@ func testCases(repo *cache.RepoCache, identity *cache.IdentityCache) ([]*testCas
 	}
 
 	// bug with comments editions
-	bugWithCommentEditions, err := repo.NewBug("bug with comments editions", "new bug")
+	bugWithCommentEditions, createOp, err := repo.NewBug("bug with comments editions", "new bug")
 	if err != nil {
 		return nil, err
 	}
 
-	createOpHash, err := bugWithCommentEditions.Snapshot().Operations[0].Hash()
+	createOpHash, err := createOp.Hash()
 	if err != nil {
 		return nil, err
 	}
@@ -100,7 +100,7 @@ func testCases(repo *cache.RepoCache, identity *cache.IdentityCache) ([]*testCas
 	}
 
 	// bug status changed
-	bugStatusChanged, err := repo.NewBug("bug status changed", "new bug")
+	bugStatusChanged, _, err := repo.NewBug("bug status changed", "new bug")
 	if err != nil {
 		return nil, err
 	}
@@ -116,7 +116,7 @@ func testCases(repo *cache.RepoCache, identity *cache.IdentityCache) ([]*testCas
 	}
 
 	// bug title changed
-	bugTitleEdited, err := repo.NewBug("bug title edited", "new bug")
+	bugTitleEdited, _, err := repo.NewBug("bug title edited", "new bug")
 	if err != nil {
 		return nil, err
 	}
@@ -214,6 +214,10 @@ func TestPushPull(t *testing.T) {
 		fmt.Println("deleted repository:", projectName)
 	}(t)
 
+	interrupt.RegisterCleaner(func() error {
+		return deleteRepository(projectName, user, token)
+	})
+
 	// initialize exporter
 	exporter := &githubExporter{}
 	err = exporter.Init(core.Configuration{
@@ -271,7 +275,7 @@ func TestPushPull(t *testing.T) {
 			}
 
 			// get bug github ID
-			bugGithubID, ok := tt.bug.Snapshot().Operations[0].GetMetadata(keyGithubId)
+			bugGithubID, ok := tt.bug.Snapshot().GetCreateMetadata(keyGithubId)
 			require.True(t, ok)
 
 			// retrive bug from backendTwo
@@ -282,7 +286,7 @@ func TestPushPull(t *testing.T) {
 			require.Len(t, importedBug.Snapshot().Operations, tt.numOrOp)
 
 			// verify bugs are taged with origin=github
-			issueOrigin, ok := importedBug.Snapshot().Operations[0].GetMetadata(keyOrigin)
+			issueOrigin, ok := importedBug.Snapshot().GetCreateMetadata(keyOrigin)
 			require.True(t, ok)
 			require.Equal(t, issueOrigin, target)
 
@@ -303,7 +307,7 @@ func generateRepoName() string {
 
 // create repository need a token with scope 'repo'
 func createRepository(project, token string) error {
-// This function use the V3 Github API because repository creation is not supported yet on the V4 API.
+	// This function use the V3 Github API because repository creation is not supported yet on the V4 API.
 	url := fmt.Sprintf("%s/user/repos", githubV3Url)
 
 	params := struct {
@@ -345,7 +349,7 @@ func createRepository(project, token string) error {
 
 // delete repository need a token with scope 'delete_repo'
 func deleteRepository(project, owner, token string) error {
-// This function use the V3 Github API because repository removal is not supported yet on the V4 API.
+	// This function use the V3 Github API because repository removal is not supported yet on the V4 API.
 	url := fmt.Sprintf("%s/repos/%s/%s", githubV3Url, owner, project)
 
 	req, err := http.NewRequest("DELETE", url, nil)

bug/snapshot.go 🔗

@@ -54,6 +54,11 @@ func (snap *Snapshot) LastEditUnix() int64 {
 	return snap.Operations[len(snap.Operations)-1].GetUnixTime()
 }
 
+// GetCreateMetadata return the creation metadata
+func (snap *Snapshot) GetCreateMetadata(key string) (string, bool) {
+	return snap.Operations[0].GetMetadata(key)
+}
+
 // SearchTimelineItem will search in the timeline for an item matching the given hash
 func (snap *Snapshot) SearchTimelineItem(hash git.Hash) (TimelineItem, error) {
 	for i := range snap.Timeline {
@@ -87,5 +92,45 @@ func (snap *Snapshot) addParticipant(participant identity.Interface) {
 	snap.Participants = append(snap.Participants, participant)
 }
 
+// HasParticipant return true if the id is a participant
+func (snap *Snapshot) HasParticipant(id string) bool {
+	for _, p := range snap.Participants {
+		if p.Id() == id {
+			return true
+		}
+	}
+	return false
+}
+
+// HasAnyParticipant return true if one of the ids is a participant
+func (snap *Snapshot) HasAnyParticipant(ids ...string) bool {
+	for _, id := range ids {
+		if snap.HasParticipant(id) {
+			return true
+		}
+	}
+	return false
+}
+
+// HasActor return true if the id is a actor
+func (snap *Snapshot) HasActor(id string) bool {
+	for _, p := range snap.Actors {
+		if p.Id() == id {
+			return true
+		}
+	}
+	return false
+}
+
+// HasAnyActor return true if one of the ids is a actor
+func (snap *Snapshot) HasAnyActor(ids ...string) bool {
+	for _, id := range ids {
+		if snap.HasActor(id) {
+			return true
+		}
+	}
+	return false
+}
+
 // Sign post method for gqlgen
 func (snap *Snapshot) IsAuthored() {}