Improve naming and functions signatures

Amine Hilaly created

expose `NewIterator` in `github` package
remove `exist` in tests cases
skip tests when env var GITHUB_TOKEN is not given

Change summary

bridge/github/github.go      |   4 
bridge/github/import.go      |  34 ++++----
bridge/github/import_test.go | 148 +++++++++++++++++-------------------
bridge/github/iterator.go    | 154 +++++++++++++++++++------------------
4 files changed, 169 insertions(+), 171 deletions(-)

Detailed changes

bridge/github/github.go 🔗

@@ -27,9 +27,9 @@ func (*Github) NewExporter() core.Exporter {
 	return nil
 }
 
-func buildClient(conf core.Configuration) *githubv4.Client {
+func buildClient(token string) *githubv4.Client {
 	src := oauth2.StaticTokenSource(
-		&oauth2.Token{AccessToken: conf[keyToken]},
+		&oauth2.Token{AccessToken: token},
 	)
 	httpClient := oauth2.NewClient(context.TODO(), src)
 

bridge/github/import.go 🔗

@@ -22,29 +22,27 @@ const (
 
 // githubImporter implement the Importer interface
 type githubImporter struct {
-	iterator *iterator
-	conf     core.Configuration
+	conf core.Configuration
 }
 
 func (gi *githubImporter) Init(conf core.Configuration) error {
 	gi.conf = conf
-	gi.iterator = newIterator(conf)
 	return nil
 }
 
 // ImportAll .
 func (gi *githubImporter) ImportAll(repo *cache.RepoCache, since time.Time) error {
-	gi.iterator.since = since
+	iterator := NewIterator(gi.conf[keyUser], gi.conf[keyProject], gi.conf[keyToken], since)
 
 	// Loop over all matching issues
-	for gi.iterator.NextIssue() {
-		issue := gi.iterator.IssueValue()
+	for iterator.NextIssue() {
+		issue := iterator.IssueValue()
 
-		fmt.Printf("importing issue: %v %v\n", gi.iterator.count, issue.Title)
+		fmt.Printf("importing issue: %v %v\n", iterator.importedIssues, issue.Title)
 		// get issue edits
 		issueEdits := []userContentEdit{}
-		for gi.iterator.NextIssueEdit() {
-			if issueEdit := gi.iterator.IssueEditValue(); issueEdit.Diff != nil && string(*issueEdit.Diff) != "" {
+		for iterator.NextIssueEdit() {
+			if issueEdit := iterator.IssueEditValue(); issueEdit.Diff != nil && string(*issueEdit.Diff) != "" {
 				issueEdits = append(issueEdits, issueEdit)
 			}
 		}
@@ -56,15 +54,15 @@ func (gi *githubImporter) ImportAll(repo *cache.RepoCache, since time.Time) erro
 		}
 
 		// loop over timeline items
-		for gi.iterator.NextTimeline() {
-			item := gi.iterator.TimelineValue()
+		for iterator.NextTimeline() {
+			item := iterator.TimelineValue()
 
 			// if item is comment
 			if item.Typename == "IssueComment" {
 				// collect all edits
 				commentEdits := []userContentEdit{}
-				for gi.iterator.NextCommentEdit() {
-					if commentEdit := gi.iterator.CommentEditValue(); commentEdit.Diff != nil && string(*commentEdit.Diff) != "" {
+				for iterator.NextCommentEdit() {
+					if commentEdit := iterator.CommentEditValue(); commentEdit.Diff != nil && string(*commentEdit.Diff) != "" {
 						commentEdits = append(commentEdits, commentEdit)
 					}
 				}
@@ -87,12 +85,12 @@ func (gi *githubImporter) ImportAll(repo *cache.RepoCache, since time.Time) erro
 		}
 	}
 
-	if err := gi.iterator.Error(); err != nil {
+	if err := iterator.Error(); err != nil {
 		fmt.Printf("import error: %v\n", err)
 		return err
 	}
 
-	fmt.Printf("Successfully imported %v issues from Github\n", gi.iterator.Count())
+	fmt.Printf("Successfully imported %v issues from Github\n", iterator.ImportedIssues())
 	return nil
 }
 
@@ -389,7 +387,7 @@ func (gi *githubImporter) ensureCommentEdit(repo *cache.RepoCache, b *cache.BugC
 	switch {
 	case edit.DeletedAt != nil:
 		// comment deletion, not supported yet
-		fmt.Println("comment deletion ....")
+		fmt.Println("comment deletion is not supported yet")
 
 	case edit.DeletedAt == nil:
 
@@ -475,7 +473,9 @@ func (gi *githubImporter) getGhost(repo *cache.RepoCache) (*cache.IdentityCache,
 		"login": githubv4.String("ghost"),
 	}
 
-	err = gi.iterator.gc.Query(context.TODO(), &q, variables)
+	gc := buildClient(gi.conf[keyToken])
+
+	err = gc.Query(context.TODO(), &q, variables)
 	if err != nil {
 		return nil, err
 	}

bridge/github/import_test.go 🔗

@@ -18,15 +18,13 @@ import (
 func Test_Importer(t *testing.T) {
 	author := identity.NewIdentity("Michael Muré", "batolettre@gmail.com")
 	tests := []struct {
-		name  string
-		exist bool
-		url   string
-		bug   *bug.Snapshot
+		name string
+		url  string
+		bug  *bug.Snapshot
 	}{
 		{
-			name:  "simple issue",
-			exist: true,
-			url:   "https://github.com/MichaelMure/git-but-test-github-bridge/issues/1",
+			name: "simple issue",
+			url:  "https://github.com/MichaelMure/git-but-test-github-bridge/issues/1",
 			bug: &bug.Snapshot{
 				Operations: []bug.Operation{
 					bug.NewCreateOp(author, 0, "simple issue", "initial comment", nil),
@@ -35,9 +33,8 @@ func Test_Importer(t *testing.T) {
 			},
 		},
 		{
-			name:  "empty issue",
-			exist: true,
-			url:   "https://github.com/MichaelMure/git-but-test-github-bridge/issues/2",
+			name: "empty issue",
+			url:  "https://github.com/MichaelMure/git-but-test-github-bridge/issues/2",
 			bug: &bug.Snapshot{
 				Operations: []bug.Operation{
 					bug.NewCreateOp(author, 0, "empty issue", "", nil),
@@ -45,9 +42,8 @@ func Test_Importer(t *testing.T) {
 			},
 		},
 		{
-			name:  "complex issue",
-			exist: true,
-			url:   "https://github.com/MichaelMure/git-but-test-github-bridge/issues/3",
+			name: "complex issue",
+			url:  "https://github.com/MichaelMure/git-but-test-github-bridge/issues/3",
 			bug: &bug.Snapshot{
 				Operations: []bug.Operation{
 					bug.NewCreateOp(author, 0, "complex issue", "initial comment", nil),
@@ -63,9 +59,8 @@ func Test_Importer(t *testing.T) {
 			},
 		},
 		{
-			name:  "editions",
-			exist: true,
-			url:   "https://github.com/MichaelMure/git-but-test-github-bridge/issues/4",
+			name: "editions",
+			url:  "https://github.com/MichaelMure/git-but-test-github-bridge/issues/4",
 			bug: &bug.Snapshot{
 				Operations: []bug.Operation{
 					bug.NewCreateOp(author, 0, "editions", "initial comment edited", nil),
@@ -76,9 +71,8 @@ func Test_Importer(t *testing.T) {
 			},
 		},
 		{
-			name:  "comment deletion",
-			exist: true,
-			url:   "https://github.com/MichaelMure/git-but-test-github-bridge/issues/5",
+			name: "comment deletion",
+			url:  "https://github.com/MichaelMure/git-but-test-github-bridge/issues/5",
 			bug: &bug.Snapshot{
 				Operations: []bug.Operation{
 					bug.NewCreateOp(author, 0, "comment deletion", "", nil),
@@ -86,9 +80,8 @@ func Test_Importer(t *testing.T) {
 			},
 		},
 		{
-			name:  "edition deletion",
-			exist: true,
-			url:   "https://github.com/MichaelMure/git-but-test-github-bridge/issues/6",
+			name: "edition deletion",
+			url:  "https://github.com/MichaelMure/git-but-test-github-bridge/issues/6",
 			bug: &bug.Snapshot{
 				Operations: []bug.Operation{
 					bug.NewCreateOp(author, 0, "edition deletion", "initial comment", nil),
@@ -99,9 +92,8 @@ func Test_Importer(t *testing.T) {
 			},
 		},
 		{
-			name:  "hidden comment",
-			exist: true,
-			url:   "https://github.com/MichaelMure/git-but-test-github-bridge/issues/7",
+			name: "hidden comment",
+			url:  "https://github.com/MichaelMure/git-but-test-github-bridge/issues/7",
 			bug: &bug.Snapshot{
 				Operations: []bug.Operation{
 					bug.NewCreateOp(author, 0, "hidden comment", "initial comment", nil),
@@ -110,9 +102,8 @@ func Test_Importer(t *testing.T) {
 			},
 		},
 		{
-			name:  "transfered issue",
-			exist: true,
-			url:   "https://github.com/MichaelMure/git-but-test-github-bridge/issues/8",
+			name: "transfered issue",
+			url:  "https://github.com/MichaelMure/git-but-test-github-bridge/issues/8",
 			bug: &bug.Snapshot{
 				Operations: []bug.Operation{
 					bug.NewCreateOp(author, 0, "transfered issue", "", nil),
@@ -139,11 +130,16 @@ func Test_Importer(t *testing.T) {
 	defer backend.Close()
 	interrupt.RegisterCleaner(backend.Close)
 
+	token := os.Getenv("GITHUB_TOKEN")
+	if token == "" {
+		t.Skip("Env var GITHUB_TOKEN missing")
+	}
+
 	importer := &githubImporter{}
 	err = importer.Init(core.Configuration{
 		"user":    "MichaelMure",
 		"project": "git-but-test-github-bridge",
-		"token":   os.Getenv("GITHUB_TOKEN"),
+		"token":   token,
 	})
 	if err != nil {
 		t.Fatal(err)
@@ -165,59 +161,53 @@ func Test_Importer(t *testing.T) {
 			}
 
 			ops := b.Snapshot().Operations
-			if tt.exist {
-				assert.Equal(t, len(tt.bug.Operations), len(b.Snapshot().Operations))
-
-				for i, op := range tt.bug.Operations {
-					switch op.(type) {
-					case *bug.CreateOperation:
-						if op2, ok := ops[i].(*bug.CreateOperation); ok {
-							assert.Equal(t, op2.Title, op.(*bug.CreateOperation).Title)
-							assert.Equal(t, op2.Message, op.(*bug.CreateOperation).Message)
-							continue
-						}
-						t.Errorf("bad operation type index = %d expected = CreationOperation", i)
-					case *bug.SetStatusOperation:
-						if op2, ok := ops[i].(*bug.SetStatusOperation); ok {
-							assert.Equal(t, op2.Status, op.(*bug.SetStatusOperation).Status)
-							continue
-						}
-						t.Errorf("bad operation type index = %d expected = SetStatusOperation", i)
-					case *bug.SetTitleOperation:
-						if op2, ok := ops[i].(*bug.SetTitleOperation); ok {
-							assert.Equal(t, op.(*bug.SetTitleOperation).Was, op2.Was)
-							assert.Equal(t, op.(*bug.SetTitleOperation).Title, op2.Title)
-							continue
-						}
-						t.Errorf("bad operation type index = %d expected = SetTitleOperation", i)
-					case *bug.LabelChangeOperation:
-						if op2, ok := ops[i].(*bug.LabelChangeOperation); ok {
-							assert.ElementsMatch(t, op.(*bug.LabelChangeOperation).Added, op2.Added)
-							assert.ElementsMatch(t, op.(*bug.LabelChangeOperation).Removed, op2.Removed)
-							continue
-						}
-						t.Errorf("bad operation type index = %d expected = ChangeLabelOperation", i)
-					case *bug.AddCommentOperation:
-						if op2, ok := ops[i].(*bug.AddCommentOperation); ok {
-							assert.Equal(t, op.(*bug.AddCommentOperation).Message, op2.Message)
-							continue
-						}
-						t.Errorf("bad operation type index = %d expected = AddCommentOperation", i)
-					case *bug.EditCommentOperation:
-						if op2, ok := ops[i].(*bug.EditCommentOperation); ok {
-							assert.Equal(t, op.(*bug.EditCommentOperation).Message, op2.Message)
-							continue
-						}
-						t.Errorf("bad operation type index = %d expected = EditCommentOperation", i)
-					default:
-
+			assert.Equal(t, len(tt.bug.Operations), len(b.Snapshot().Operations))
+
+			for i, op := range tt.bug.Operations {
+				switch op.(type) {
+				case *bug.CreateOperation:
+					if op2, ok := ops[i].(*bug.CreateOperation); ok {
+						assert.Equal(t, op2.Title, op.(*bug.CreateOperation).Title)
+						assert.Equal(t, op2.Message, op.(*bug.CreateOperation).Message)
+						continue
+					}
+					t.Errorf("bad operation type index = %d expected = CreationOperation", i)
+				case *bug.SetStatusOperation:
+					if op2, ok := ops[i].(*bug.SetStatusOperation); ok {
+						assert.Equal(t, op2.Status, op.(*bug.SetStatusOperation).Status)
+						continue
+					}
+					t.Errorf("bad operation type index = %d expected = SetStatusOperation", i)
+				case *bug.SetTitleOperation:
+					if op2, ok := ops[i].(*bug.SetTitleOperation); ok {
+						assert.Equal(t, op.(*bug.SetTitleOperation).Was, op2.Was)
+						assert.Equal(t, op.(*bug.SetTitleOperation).Title, op2.Title)
+						continue
 					}
+					t.Errorf("bad operation type index = %d expected = SetTitleOperation", i)
+				case *bug.LabelChangeOperation:
+					if op2, ok := ops[i].(*bug.LabelChangeOperation); ok {
+						assert.ElementsMatch(t, op.(*bug.LabelChangeOperation).Added, op2.Added)
+						assert.ElementsMatch(t, op.(*bug.LabelChangeOperation).Removed, op2.Removed)
+						continue
+					}
+					t.Errorf("bad operation type index = %d expected = ChangeLabelOperation", i)
+				case *bug.AddCommentOperation:
+					if op2, ok := ops[i].(*bug.AddCommentOperation); ok {
+						assert.Equal(t, op.(*bug.AddCommentOperation).Message, op2.Message)
+						continue
+					}
+					t.Errorf("bad operation type index = %d expected = AddCommentOperation", i)
+				case *bug.EditCommentOperation:
+					if op2, ok := ops[i].(*bug.EditCommentOperation); ok {
+						assert.Equal(t, op.(*bug.EditCommentOperation).Message, op2.Message)
+						continue
+					}
+					t.Errorf("bad operation type index = %d expected = EditCommentOperation", i)
+				default:
+					panic("Unknown operation type")
 				}
-
-			} else {
-				assert.Equal(t, b, nil)
 			}
 		})
 	}
-
 }

bridge/github/iterator.go 🔗

@@ -4,7 +4,6 @@ import (
 	"context"
 	"time"
 
-	"github.com/MichaelMure/git-bug/bridge/core"
 	"github.com/shurcooL/githubv4"
 )
 
@@ -50,8 +49,8 @@ type iterator struct {
 	// sticky error
 	err error
 
-	// count to keep track of the number of imported issues
-	count int
+	// number of imported issues
+	importedIssues int
 
 	// timeline iterator
 	timeline timelineIterator
@@ -63,32 +62,32 @@ type iterator struct {
 	commentEdit commentEditIterator
 }
 
-func newIterator(conf core.Configuration) *iterator {
+func NewIterator(user, project, token string, since time.Time) *iterator {
 	return &iterator{
-		gc:       buildClient(conf),
+		gc:       buildClient(token),
+		since:    since,
 		capacity: 10,
-		count:    0,
 		timeline: timelineIterator{
 			index:       -1,
 			issueEdit:   indexer{-1},
 			commentEdit: indexer{-1},
 			variables: map[string]interface{}{
-				"owner": githubv4.String(conf["user"]),
-				"name":  githubv4.String(conf["project"]),
+				"owner": githubv4.String(user),
+				"name":  githubv4.String(project),
 			},
 		},
 		commentEdit: commentEditIterator{
 			index: -1,
 			variables: map[string]interface{}{
-				"owner": githubv4.String(conf["user"]),
-				"name":  githubv4.String(conf["project"]),
+				"owner": githubv4.String(user),
+				"name":  githubv4.String(project),
 			},
 		},
 		issueEdit: issueEditIterator{
 			index: -1,
 			variables: map[string]interface{}{
-				"owner": githubv4.String(conf["user"]),
-				"name":  githubv4.String(conf["project"]),
+				"owner": githubv4.String(user),
+				"name":  githubv4.String(project),
 			},
 		},
 	}
@@ -130,10 +129,11 @@ func (i *iterator) initCommentEditQueryVariables() {
 // reverse UserContentEdits arrays in both of the issue and
 // comment timelines
 func (i *iterator) reverseTimelineEditNodes() {
-	reverseEdits(i.timeline.query.Repository.Issues.Nodes[0].UserContentEdits.Nodes)
-	for index, ce := range i.timeline.query.Repository.Issues.Nodes[0].Timeline.Edges {
-		if ce.Node.Typename == "IssueComment" && len(i.timeline.query.Repository.Issues.Nodes[0].Timeline.Edges) != 0 {
-			reverseEdits(i.timeline.query.Repository.Issues.Nodes[0].Timeline.Edges[index].Node.IssueComment.UserContentEdits.Nodes)
+	node := i.timeline.query.Repository.Issues.Nodes[0]
+	reverseEdits(node.UserContentEdits.Nodes)
+	for index, ce := range node.Timeline.Edges {
+		if ce.Node.Typename == "IssueComment" && len(node.Timeline.Edges) != 0 {
+			reverseEdits(node.Timeline.Edges[index].Node.IssueComment.UserContentEdits.Nodes)
 		}
 	}
 }
@@ -143,19 +143,34 @@ func (i *iterator) Error() error {
 	return i.err
 }
 
-// Count return number of issues we iterated over
-func (i *iterator) Count() int {
-	return i.count
+// ImportedIssues return the number of issues we iterated over
+func (i *iterator) ImportedIssues() int {
+	return i.importedIssues
+}
+
+func (i *iterator) queryIssue() bool {
+	if err := i.gc.Query(context.TODO(), &i.timeline.query, i.timeline.variables); err != nil {
+		i.err = err
+		return false
+	}
+
+	if len(i.timeline.query.Repository.Issues.Nodes) == 0 {
+		return false
+	}
+
+	i.reverseTimelineEditNodes()
+	i.importedIssues++
+	return true
 }
 
 // Next issue
 func (i *iterator) NextIssue() bool {
 	// we make the first move
-	if i.count == 0 {
+	if i.importedIssues == 0 {
 
 		// init variables and goto queryIssue block
 		i.initTimelineQueryVariables()
-		goto queryIssue
+		return i.queryIssue()
 	}
 
 	if i.err != nil {
@@ -175,19 +190,7 @@ func (i *iterator) NextIssue() bool {
 	i.timeline.lastEndCursor = i.timeline.query.Repository.Issues.Nodes[0].Timeline.PageInfo.EndCursor
 
 	// query issue block
-queryIssue:
-	if err := i.gc.Query(context.TODO(), &i.timeline.query, i.timeline.variables); err != nil {
-		i.err = err
-		return false
-	}
-
-	if len(i.timeline.query.Repository.Issues.Nodes) == 0 {
-		return false
-	}
-
-	i.reverseTimelineEditNodes()
-	i.count++
-	return true
+	return i.queryIssue()
 }
 
 func (i *iterator) IssueValue() issueTimeline {
@@ -230,6 +233,27 @@ func (i *iterator) TimelineValue() timelineItem {
 	return i.timeline.query.Repository.Issues.Nodes[0].Timeline.Edges[i.timeline.index].Node
 }
 
+func (i *iterator) queryIssueEdit() bool {
+	if err := i.gc.Query(context.TODO(), &i.issueEdit.query, i.issueEdit.variables); err != nil {
+		i.err = err
+		//i.timeline.issueEdit.index = -1
+		return false
+	}
+
+	// reverse issue edits because github
+	reverseEdits(i.issueEdit.query.Repository.Issues.Nodes[0].UserContentEdits.Nodes)
+
+	// this is not supposed to happen
+	if len(i.issueEdit.query.Repository.Issues.Nodes[0].UserContentEdits.Nodes) == 0 {
+		i.timeline.issueEdit.index = -1
+		return false
+	}
+
+	i.issueEdit.index = 0
+	i.timeline.issueEdit.index = -2
+	return true
+}
+
 func (i *iterator) NextIssueEdit() bool {
 	if i.err != nil {
 		return false
@@ -251,7 +275,7 @@ func (i *iterator) NextIssueEdit() bool {
 
 		// if there is more edits, query them
 		i.issueEdit.variables["issueEditBefore"] = i.issueEdit.query.Repository.Issues.Nodes[0].UserContentEdits.PageInfo.StartCursor
-		goto queryIssueEdit
+		return i.queryIssueEdit()
 	}
 
 	// if there is no edits
@@ -273,26 +297,7 @@ func (i *iterator) NextIssueEdit() bool {
 	// if there is more edits, query them
 	i.initIssueEditQueryVariables()
 	i.issueEdit.variables["issueEditBefore"] = i.timeline.query.Repository.Issues.Nodes[0].UserContentEdits.PageInfo.StartCursor
-
-queryIssueEdit:
-	if err := i.gc.Query(context.TODO(), &i.issueEdit.query, i.issueEdit.variables); err != nil {
-		i.err = err
-		//i.timeline.issueEdit.index = -1
-		return false
-	}
-
-	// reverse issue edits because github
-	reverseEdits(i.issueEdit.query.Repository.Issues.Nodes[0].UserContentEdits.Nodes)
-
-	// this is not supposed to happen
-	if len(i.issueEdit.query.Repository.Issues.Nodes[0].UserContentEdits.Nodes) == 0 {
-		i.timeline.issueEdit.index = -1
-		return false
-	}
-
-	i.issueEdit.index = 0
-	i.timeline.issueEdit.index = -2
-	return true
+	return i.queryIssueEdit()
 }
 
 func (i *iterator) IssueEditValue() userContentEdit {
@@ -305,6 +310,25 @@ func (i *iterator) IssueEditValue() userContentEdit {
 	return i.timeline.query.Repository.Issues.Nodes[0].UserContentEdits.Nodes[i.timeline.issueEdit.index]
 }
 
+func (i *iterator) queryCommentEdit() bool {
+	if err := i.gc.Query(context.TODO(), &i.commentEdit.query, i.commentEdit.variables); err != nil {
+		i.err = err
+		return false
+	}
+
+	// this is not supposed to happen
+	if len(i.commentEdit.query.Repository.Issues.Nodes[0].Timeline.Nodes[0].IssueComment.UserContentEdits.Nodes) == 0 {
+		i.timeline.commentEdit.index = -1
+		return false
+	}
+
+	reverseEdits(i.commentEdit.query.Repository.Issues.Nodes[0].Timeline.Nodes[0].IssueComment.UserContentEdits.Nodes)
+
+	i.commentEdit.index = 0
+	i.timeline.commentEdit.index = -2
+	return true
+}
+
 func (i *iterator) NextCommentEdit() bool {
 	if i.err != nil {
 		return false
@@ -326,7 +350,7 @@ func (i *iterator) NextCommentEdit() bool {
 
 		// if there is more comment edits, query them
 		i.commentEdit.variables["commentEditBefore"] = i.commentEdit.query.Repository.Issues.Nodes[0].Timeline.Nodes[0].IssueComment.UserContentEdits.PageInfo.StartCursor
-		goto queryCommentEdit
+		return i.queryCommentEdit()
 	}
 
 	// if there is no comment edits
@@ -354,23 +378,7 @@ func (i *iterator) NextCommentEdit() bool {
 
 	i.commentEdit.variables["commentEditBefore"] = i.timeline.query.Repository.Issues.Nodes[0].Timeline.Edges[i.timeline.index].Node.IssueComment.UserContentEdits.PageInfo.StartCursor
 
-queryCommentEdit:
-	if err := i.gc.Query(context.TODO(), &i.commentEdit.query, i.commentEdit.variables); err != nil {
-		i.err = err
-		return false
-	}
-
-	// this is not supposed to happen
-	if len(i.commentEdit.query.Repository.Issues.Nodes[0].Timeline.Nodes[0].IssueComment.UserContentEdits.Nodes) == 0 {
-		i.timeline.commentEdit.index = -1
-		return false
-	}
-
-	reverseEdits(i.commentEdit.query.Repository.Issues.Nodes[0].Timeline.Nodes[0].IssueComment.UserContentEdits.Nodes)
-
-	i.commentEdit.index = 0
-	i.timeline.commentEdit.index = -2
-	return true
+	return i.queryCommentEdit()
 }
 
 func (i *iterator) CommentEditValue() userContentEdit {