aboutsummaryrefslogtreecommitdiffstats
path: root/bug
diff options
context:
space:
mode:
Diffstat (limited to 'bug')
-rw-r--r--bug/bug.go59
-rw-r--r--bug/bug_actions.go2
-rw-r--r--bug/comment.go10
-rw-r--r--bug/interface.go6
-rw-r--r--bug/op_add_comment.go36
-rw-r--r--bug/op_add_comment_test.go6
-rw-r--r--bug/op_create.go39
-rw-r--r--bug/op_create_test.go11
-rw-r--r--bug/op_edit_comment.go51
-rw-r--r--bug/op_edit_comment_test.go23
-rw-r--r--bug/op_label_change.go38
-rw-r--r--bug/op_label_change_test.go5
-rw-r--r--bug/op_noop.go25
-rw-r--r--bug/op_noop_test.go5
-rw-r--r--bug/op_set_metadata.go47
-rw-r--r--bug/op_set_metadata_test.go19
-rw-r--r--bug/op_set_status.go35
-rw-r--r--bug/op_set_status_test.go5
-rw-r--r--bug/op_set_title.go38
-rw-r--r--bug/op_set_title_test.go5
-rw-r--r--bug/operation.go76
-rw-r--r--bug/operation_pack_test.go10
-rw-r--r--bug/operation_test.go14
-rw-r--r--bug/snapshot.go24
-rw-r--r--bug/timeline.go9
25 files changed, 213 insertions, 385 deletions
diff --git a/bug/bug.go b/bug/bug.go
index 7e28954f..d22a2f5e 100644
--- a/bug/bug.go
+++ b/bug/bug.go
@@ -8,6 +8,7 @@ import (
"github.com/pkg/errors"
+ "github.com/MichaelMure/git-bug/entity"
"github.com/MichaelMure/git-bug/identity"
"github.com/MichaelMure/git-bug/repository"
"github.com/MichaelMure/git-bug/util/git"
@@ -26,20 +27,24 @@ const createClockEntryPattern = "create-clock-%d"
const editClockEntryPrefix = "edit-clock-"
const editClockEntryPattern = "edit-clock-%d"
-const idLength = 40
-const humanIdLength = 7
-
var ErrBugNotExist = errors.New("bug doesn't exist")
type ErrMultipleMatch struct {
- Matching []string
+ Matching []entity.Id
}
func (e ErrMultipleMatch) Error() string {
- return fmt.Sprintf("Multiple matching bug found:\n%s", strings.Join(e.Matching, "\n"))
+ matching := make([]string, len(e.Matching))
+
+ for i, match := range e.Matching {
+ matching[i] = match.String()
+ }
+
+ return fmt.Sprintf("Multiple matching bug found:\n%s", strings.Join(matching, "\n"))
}
var _ Interface = &Bug{}
+var _ entity.Interface = &Bug{}
// Bug hold the data of a bug thread, organized in a way close to
// how it will be persisted inside Git. This is the data structure
@@ -53,7 +58,7 @@ type Bug struct {
editTime lamport.Time
// Id used as unique identifier
- id string
+ id entity.Id
lastCommit git.Hash
rootPack git.Hash
@@ -82,10 +87,10 @@ func FindLocalBug(repo repository.ClockedRepo, prefix string) (*Bug, error) {
}
// preallocate but empty
- matching := make([]string, 0, 5)
+ matching := make([]entity.Id, 0, 5)
for _, id := range ids {
- if strings.HasPrefix(id, prefix) {
+ if id.HasPrefix(prefix) {
matching = append(matching, id)
}
}
@@ -102,8 +107,8 @@ func FindLocalBug(repo repository.ClockedRepo, prefix string) (*Bug, error) {
}
// ReadLocalBug will read a local bug from its hash
-func ReadLocalBug(repo repository.ClockedRepo, id string) (*Bug, error) {
- ref := bugsRefPattern + id
+func ReadLocalBug(repo repository.ClockedRepo, id entity.Id) (*Bug, error) {
+ ref := bugsRefPattern + id.String()
return readBug(repo, ref)
}
@@ -116,10 +121,10 @@ func ReadRemoteBug(repo repository.ClockedRepo, remote string, id string) (*Bug,
// readBug will read and parse a Bug from git
func readBug(repo repository.ClockedRepo, ref string) (*Bug, error) {
refSplit := strings.Split(ref, "/")
- id := refSplit[len(refSplit)-1]
+ id := entity.Id(refSplit[len(refSplit)-1])
- if len(id) != idLength {
- return nil, fmt.Errorf("invalid ref length")
+ if err := id.Validate(); err != nil {
+ return nil, errors.Wrap(err, "invalid ref ")
}
hashes, err := repo.ListCommits(ref)
@@ -278,7 +283,7 @@ func readAllBugs(repo repository.ClockedRepo, refPrefix string) <-chan StreamedB
}
// ListLocalIds list all the available local bug ids
-func ListLocalIds(repo repository.Repo) ([]string, error) {
+func ListLocalIds(repo repository.Repo) ([]entity.Id, error) {
refs, err := repo.ListRefs(bugsRefPattern)
if err != nil {
return nil, err
@@ -287,12 +292,12 @@ func ListLocalIds(repo repository.Repo) ([]string, error) {
return refsToIds(refs), nil
}
-func refsToIds(refs []string) []string {
- ids := make([]string, len(refs))
+func refsToIds(refs []string) []entity.Id {
+ ids := make([]entity.Id, len(refs))
for i, ref := range refs {
split := strings.Split(ref, "/")
- ids[i] = split[len(split)-1]
+ ids[i] = entity.Id(split[len(split)-1])
}
return ids
@@ -325,8 +330,8 @@ func (bug *Bug) Validate() error {
return fmt.Errorf("first operation should be a Create op")
}
- // The bug ID should be the hash of the first commit
- if len(bug.packs) > 0 && string(bug.packs[0].commitHash) != bug.id {
+ // The bug Id should be the hash of the first commit
+ if len(bug.packs) > 0 && string(bug.packs[0].commitHash) != bug.id.String() {
return fmt.Errorf("bug id should be the first commit hash")
}
@@ -456,7 +461,7 @@ func (bug *Bug) Commit(repo repository.ClockedRepo) error {
// if it was the first commit, use the commit hash as bug id
if bug.id == "" {
- bug.id = string(hash)
+ bug.id = entity.Id(hash)
}
// Create or update the Git reference for this bug
@@ -594,7 +599,7 @@ func (bug *Bug) Merge(repo repository.Repo, other Interface) (bool, error) {
}
// Update the git ref
- err = repo.UpdateRef(bugsRefPattern+bug.id, bug.lastCommit)
+ err = repo.UpdateRef(bugsRefPattern+bug.id.String(), bug.lastCommit)
if err != nil {
return false, err
}
@@ -603,7 +608,7 @@ func (bug *Bug) Merge(repo repository.Repo, other Interface) (bool, error) {
}
// Id return the Bug identifier
-func (bug *Bug) Id() string {
+func (bug *Bug) Id() entity.Id {
if bug.id == "" {
// simply panic as it would be a coding error
// (using an id of a bug not stored yet)
@@ -612,16 +617,6 @@ func (bug *Bug) Id() string {
return bug.id
}
-// HumanId return the Bug identifier truncated for human consumption
-func (bug *Bug) HumanId() string {
- return FormatHumanID(bug.Id())
-}
-
-func FormatHumanID(id string) string {
- format := fmt.Sprintf("%%.%ds", humanIdLength)
- return fmt.Sprintf(format, id)
-}
-
// CreateLamportTime return the Lamport time of creation
func (bug *Bug) CreateLamportTime() lamport.Time {
return bug.createTime
diff --git a/bug/bug_actions.go b/bug/bug_actions.go
index b26d080f..e6fc12d5 100644
--- a/bug/bug_actions.go
+++ b/bug/bug_actions.go
@@ -81,7 +81,7 @@ func MergeAll(repo repository.ClockedRepo, remote string) <-chan entity.MergeRes
continue
}
- localRef := bugsRefPattern + remoteBug.Id()
+ localRef := bugsRefPattern + remoteBug.Id().String()
localExist, err := repo.RefExist(localRef)
if err != nil {
diff --git a/bug/comment.go b/bug/comment.go
index 5db0b18d..47c1ff05 100644
--- a/bug/comment.go
+++ b/bug/comment.go
@@ -1,6 +1,7 @@
package bug
import (
+ "github.com/MichaelMure/git-bug/entity"
"github.com/MichaelMure/git-bug/identity"
"github.com/MichaelMure/git-bug/util/git"
"github.com/MichaelMure/git-bug/util/timestamp"
@@ -9,7 +10,7 @@ import (
// Comment represent a comment in a Bug
type Comment struct {
- id string
+ id entity.Id
Author identity.Interface
Message string
Files []git.Hash
@@ -20,7 +21,7 @@ type Comment struct {
}
// Id return the Comment identifier
-func (c Comment) Id() string {
+func (c Comment) Id() entity.Id {
if c.id == "" {
// simply panic as it would be a coding error
// (using an id of an identity not stored yet)
@@ -29,11 +30,6 @@ func (c Comment) Id() string {
return c.id
}
-// HumanId return the Comment identifier truncated for human consumption
-func (c Comment) HumanId() string {
- return FormatHumanID(c.Id())
-}
-
// FormatTimeRel format the UnixTime of the comment for human consumption
func (c Comment) FormatTimeRel() string {
return humanize.Time(c.UnixTime.Time())
diff --git a/bug/interface.go b/bug/interface.go
index 186c26fc..8266e99e 100644
--- a/bug/interface.go
+++ b/bug/interface.go
@@ -1,16 +1,14 @@
package bug
import (
+ "github.com/MichaelMure/git-bug/entity"
"github.com/MichaelMure/git-bug/repository"
"github.com/MichaelMure/git-bug/util/lamport"
)
type Interface interface {
// Id return the Bug identifier
- Id() string
-
- // HumanId return the Bug identifier truncated for human consumption
- HumanId() string
+ Id() entity.Id
// Validate check if the Bug data is valid
Validate() error
diff --git a/bug/op_add_comment.go b/bug/op_add_comment.go
index 1ff2ae87..e16ea0dd 100644
--- a/bug/op_add_comment.go
+++ b/bug/op_add_comment.go
@@ -4,6 +4,7 @@ import (
"encoding/json"
"fmt"
+ "github.com/MichaelMure/git-bug/entity"
"github.com/MichaelMure/git-bug/identity"
"github.com/MichaelMure/git-bug/util/git"
"github.com/MichaelMure/git-bug/util/text"
@@ -15,16 +16,16 @@ var _ Operation = &AddCommentOperation{}
// AddCommentOperation will add a new comment in the bug
type AddCommentOperation struct {
OpBase
- Message string
+ Message string `json:"message"`
// TODO: change for a map[string]util.hash to store the filename ?
- Files []git.Hash
+ Files []git.Hash `json:"files"`
}
func (op *AddCommentOperation) base() *OpBase {
return &op.OpBase
}
-func (op *AddCommentOperation) ID() string {
+func (op *AddCommentOperation) Id() entity.Id {
return idOperation(op)
}
@@ -33,7 +34,7 @@ func (op *AddCommentOperation) Apply(snapshot *Snapshot) {
snapshot.addParticipant(op.Author)
comment := Comment{
- id: op.ID(),
+ id: op.Id(),
Message: op.Message,
Author: op.Author,
Files: op.Files,
@@ -43,7 +44,7 @@ func (op *AddCommentOperation) Apply(snapshot *Snapshot) {
snapshot.Comments = append(snapshot.Comments, comment)
item := &AddCommentTimelineItem{
- CommentTimelineItem: NewCommentTimelineItem(op.ID(), comment),
+ CommentTimelineItem: NewCommentTimelineItem(op.Id(), comment),
}
snapshot.Timeline = append(snapshot.Timeline, item)
@@ -65,28 +66,9 @@ func (op *AddCommentOperation) Validate() error {
return nil
}
-// Workaround to avoid the inner OpBase.MarshalJSON overriding the outer op
-// MarshalJSON
-func (op *AddCommentOperation) MarshalJSON() ([]byte, error) {
- base, err := json.Marshal(op.OpBase)
- if err != nil {
- return nil, err
- }
-
- // revert back to a flat map to be able to add our own fields
- var data map[string]interface{}
- if err := json.Unmarshal(base, &data); err != nil {
- return nil, err
- }
-
- data["message"] = op.Message
- data["files"] = op.Files
-
- return json.Marshal(data)
-}
-
-// Workaround to avoid the inner OpBase.MarshalJSON overriding the outer op
-// MarshalJSON
+// UnmarshalJSON is a two step JSON unmarshaling
+// This workaround is necessary to avoid the inner OpBase.MarshalJSON
+// overriding the outer op's MarshalJSON
func (op *AddCommentOperation) UnmarshalJSON(data []byte) error {
// Unmarshal OpBase and the op separately
diff --git a/bug/op_add_comment_test.go b/bug/op_add_comment_test.go
index a1789aa0..55bafe36 100644
--- a/bug/op_add_comment_test.go
+++ b/bug/op_add_comment_test.go
@@ -2,6 +2,7 @@ package bug
import (
"encoding/json"
+ "fmt"
"testing"
"time"
@@ -21,8 +22,9 @@ func TestAddCommentSerialize(t *testing.T) {
err = json.Unmarshal(data, &after)
assert.NoError(t, err)
- // enforce creating the ID
- before.ID()
+ // enforce creating the IDs
+ before.Id()
+ rene.Id()
assert.Equal(t, before, &after)
}
diff --git a/bug/op_create.go b/bug/op_create.go
index e3d0a63a..0da95d4d 100644
--- a/bug/op_create.go
+++ b/bug/op_create.go
@@ -5,6 +5,7 @@ import (
"fmt"
"strings"
+ "github.com/MichaelMure/git-bug/entity"
"github.com/MichaelMure/git-bug/identity"
"github.com/MichaelMure/git-bug/util/git"
"github.com/MichaelMure/git-bug/util/text"
@@ -16,16 +17,16 @@ var _ Operation = &CreateOperation{}
// CreateOperation define the initial creation of a bug
type CreateOperation struct {
OpBase
- Title string
- Message string
- Files []git.Hash
+ Title string `json:"title"`
+ Message string `json:"message"`
+ Files []git.Hash `json:"files"`
}
func (op *CreateOperation) base() *OpBase {
return &op.OpBase
}
-func (op *CreateOperation) ID() string {
+func (op *CreateOperation) Id() entity.Id {
return idOperation(op)
}
@@ -36,7 +37,7 @@ func (op *CreateOperation) Apply(snapshot *Snapshot) {
snapshot.Title = op.Title
comment := Comment{
- id: op.ID(),
+ id: op.Id(),
Message: op.Message,
Author: op.Author,
UnixTime: timestamp.Timestamp(op.UnixTime),
@@ -48,7 +49,7 @@ func (op *CreateOperation) Apply(snapshot *Snapshot) {
snapshot.Timeline = []TimelineItem{
&CreateTimelineItem{
- CommentTimelineItem: NewCommentTimelineItem(op.ID(), comment),
+ CommentTimelineItem: NewCommentTimelineItem(op.Id(), comment),
},
}
}
@@ -81,29 +82,9 @@ func (op *CreateOperation) Validate() error {
return nil
}
-// Workaround to avoid the inner OpBase.MarshalJSON overriding the outer op
-// MarshalJSON
-func (op *CreateOperation) MarshalJSON() ([]byte, error) {
- base, err := json.Marshal(op.OpBase)
- if err != nil {
- return nil, err
- }
-
- // revert back to a flat map to be able to add our own fields
- var data map[string]interface{}
- if err := json.Unmarshal(base, &data); err != nil {
- return nil, err
- }
-
- data["title"] = op.Title
- data["message"] = op.Message
- data["files"] = op.Files
-
- return json.Marshal(data)
-}
-
-// Workaround to avoid the inner OpBase.MarshalJSON overriding the outer op
-// MarshalJSON
+// UnmarshalJSON is a two step JSON unmarshaling
+// This workaround is necessary to avoid the inner OpBase.MarshalJSON
+// overriding the outer op's MarshalJSON
func (op *CreateOperation) UnmarshalJSON(data []byte) error {
// Unmarshal OpBase and the op separately
diff --git a/bug/op_create_test.go b/bug/op_create_test.go
index 20c8ec69..ec53b04b 100644
--- a/bug/op_create_test.go
+++ b/bug/op_create_test.go
@@ -20,11 +20,11 @@ func TestCreate(t *testing.T) {
create.Apply(&snapshot)
- id := create.ID()
- assert.True(t, IDIsValid(id))
+ id := create.Id()
+ assert.NoError(t, id.Validate())
comment := Comment{
- id: string(id),
+ id: id,
Author: rene,
Message: "message",
UnixTime: timestamp.Timestamp(create.UnixTime),
@@ -61,8 +61,9 @@ func TestCreateSerialize(t *testing.T) {
err = json.Unmarshal(data, &after)
assert.NoError(t, err)
- // enforce creating the ID
- before.ID()
+ // enforce creating the IDs
+ before.Id()
+ rene.Id()
assert.Equal(t, before, &after)
}
diff --git a/bug/op_edit_comment.go b/bug/op_edit_comment.go
index 2f269b02..a37ce8f7 100644
--- a/bug/op_edit_comment.go
+++ b/bug/op_edit_comment.go
@@ -4,6 +4,9 @@ import (
"encoding/json"
"fmt"
+ "github.com/pkg/errors"
+
+ "github.com/MichaelMure/git-bug/entity"
"github.com/MichaelMure/git-bug/identity"
"github.com/MichaelMure/git-bug/util/timestamp"
@@ -16,16 +19,16 @@ var _ Operation = &EditCommentOperation{}
// EditCommentOperation will change a comment in the bug
type EditCommentOperation struct {
OpBase
- Target string
- Message string
- Files []git.Hash
+ Target entity.Id `json:"target"`
+ Message string `json:"message"`
+ Files []git.Hash `json:"files"`
}
func (op *EditCommentOperation) base() *OpBase {
return &op.OpBase
}
-func (op *EditCommentOperation) ID() string {
+func (op *EditCommentOperation) Id() entity.Id {
return idOperation(op)
}
@@ -38,7 +41,7 @@ func (op *EditCommentOperation) Apply(snapshot *Snapshot) {
var target TimelineItem
for i, item := range snapshot.Timeline {
- if item.ID() == op.Target {
+ if item.Id() == op.Target {
target = snapshot.Timeline[i]
break
}
@@ -86,8 +89,8 @@ func (op *EditCommentOperation) Validate() error {
return err
}
- if !IDIsValid(op.Target) {
- return fmt.Errorf("target hash is invalid")
+ if err := op.Target.Validate(); err != nil {
+ return errors.Wrap(err, "target hash is invalid")
}
if !text.Safe(op.Message) {
@@ -97,29 +100,9 @@ func (op *EditCommentOperation) Validate() error {
return nil
}
-// Workaround to avoid the inner OpBase.MarshalJSON overriding the outer op
-// MarshalJSON
-func (op *EditCommentOperation) MarshalJSON() ([]byte, error) {
- base, err := json.Marshal(op.OpBase)
- if err != nil {
- return nil, err
- }
-
- // revert back to a flat map to be able to add our own fields
- var data map[string]interface{}
- if err := json.Unmarshal(base, &data); err != nil {
- return nil, err
- }
-
- data["target"] = op.Target
- data["message"] = op.Message
- data["files"] = op.Files
-
- return json.Marshal(data)
-}
-
-// Workaround to avoid the inner OpBase.MarshalJSON overriding the outer op
-// MarshalJSON
+// UnmarshalJSON is a two step JSON unmarshaling
+// This workaround is necessary to avoid the inner OpBase.MarshalJSON
+// overriding the outer op's MarshalJSON
func (op *EditCommentOperation) UnmarshalJSON(data []byte) error {
// Unmarshal OpBase and the op separately
@@ -130,7 +113,7 @@ func (op *EditCommentOperation) UnmarshalJSON(data []byte) error {
}
aux := struct {
- Target string `json:"target"`
+ Target entity.Id `json:"target"`
Message string `json:"message"`
Files []git.Hash `json:"files"`
}{}
@@ -151,7 +134,7 @@ func (op *EditCommentOperation) UnmarshalJSON(data []byte) error {
// Sign post method for gqlgen
func (op *EditCommentOperation) IsAuthored() {}
-func NewEditCommentOp(author identity.Interface, unixTime int64, target string, message string, files []git.Hash) *EditCommentOperation {
+func NewEditCommentOp(author identity.Interface, unixTime int64, target entity.Id, message string, files []git.Hash) *EditCommentOperation {
return &EditCommentOperation{
OpBase: newOpBase(EditCommentOp, author, unixTime),
Target: target,
@@ -161,11 +144,11 @@ func NewEditCommentOp(author identity.Interface, unixTime int64, target string,
}
// Convenience function to apply the operation
-func EditComment(b Interface, author identity.Interface, unixTime int64, target string, message string) (*EditCommentOperation, error) {
+func EditComment(b Interface, author identity.Interface, unixTime int64, target entity.Id, message string) (*EditCommentOperation, error) {
return EditCommentWithFiles(b, author, unixTime, target, message, nil)
}
-func EditCommentWithFiles(b Interface, author identity.Interface, unixTime int64, target string, message string, files []git.Hash) (*EditCommentOperation, error) {
+func EditCommentWithFiles(b Interface, author identity.Interface, unixTime int64, target entity.Id, message string, files []git.Hash) (*EditCommentOperation, error) {
editCommentOp := NewEditCommentOp(author, unixTime, target, message, files)
if err := editCommentOp.Validate(); err != nil {
return nil, err
diff --git a/bug/op_edit_comment_test.go b/bug/op_edit_comment_test.go
index 9e8739da..abd550cb 100644
--- a/bug/op_edit_comment_test.go
+++ b/bug/op_edit_comment_test.go
@@ -20,14 +20,14 @@ func TestEdit(t *testing.T) {
create := NewCreateOp(rene, unix, "title", "create", nil)
create.Apply(&snapshot)
- hash1 := create.ID()
- require.True(t, IDIsValid(hash1))
+ id1 := create.Id()
+ require.NoError(t, id1.Validate())
comment1 := NewAddCommentOp(rene, unix, "comment 1", nil)
comment1.Apply(&snapshot)
- hash2 := comment1.ID()
- require.True(t, IDIsValid(hash2))
+ id2 := comment1.Id()
+ require.NoError(t, id2.Validate())
// add another unrelated op in between
setTitle := NewSetTitleOp(rene, unix, "edited title", "title")
@@ -36,10 +36,10 @@ func TestEdit(t *testing.T) {
comment2 := NewAddCommentOp(rene, unix, "comment 2", nil)
comment2.Apply(&snapshot)
- hash3 := comment2.ID()
- require.True(t, IDIsValid(hash3))
+ id3 := comment2.Id()
+ require.NoError(t, id3.Validate())
- edit := NewEditCommentOp(rene, unix, hash1, "create edited", nil)
+ edit := NewEditCommentOp(rene, unix, id1, "create edited", nil)
edit.Apply(&snapshot)
assert.Equal(t, len(snapshot.Timeline), 4)
@@ -50,7 +50,7 @@ func TestEdit(t *testing.T) {
assert.Equal(t, snapshot.Comments[1].Message, "comment 1")
assert.Equal(t, snapshot.Comments[2].Message, "comment 2")
- edit2 := NewEditCommentOp(rene, unix, hash2, "comment 1 edited", nil)
+ edit2 := NewEditCommentOp(rene, unix, id2, "comment 1 edited", nil)
edit2.Apply(&snapshot)
assert.Equal(t, len(snapshot.Timeline), 4)
@@ -61,7 +61,7 @@ func TestEdit(t *testing.T) {
assert.Equal(t, snapshot.Comments[1].Message, "comment 1 edited")
assert.Equal(t, snapshot.Comments[2].Message, "comment 2")
- edit3 := NewEditCommentOp(rene, unix, hash3, "comment 2 edited", nil)
+ edit3 := NewEditCommentOp(rene, unix, id3, "comment 2 edited", nil)
edit3.Apply(&snapshot)
assert.Equal(t, len(snapshot.Timeline), 4)
@@ -85,8 +85,9 @@ func TestEditCommentSerialize(t *testing.T) {
err = json.Unmarshal(data, &after)
assert.NoError(t, err)
- // enforce creating the ID
- before.ID()
+ // enforce creating the IDs
+ before.Id()
+ rene.Id()
assert.Equal(t, before, &after)
}
diff --git a/bug/op_label_change.go b/bug/op_label_change.go
index 5afcceb7..c911de26 100644
--- a/bug/op_label_change.go
+++ b/bug/op_label_change.go
@@ -7,6 +7,7 @@ import (
"github.com/pkg/errors"
+ "github.com/MichaelMure/git-bug/entity"
"github.com/MichaelMure/git-bug/identity"
"github.com/MichaelMure/git-bug/util/timestamp"
)
@@ -16,15 +17,15 @@ var _ Operation = &LabelChangeOperation{}
// LabelChangeOperation define a Bug operation to add or remove labels
type LabelChangeOperation struct {
OpBase
- Added []Label
- Removed []Label
+ Added []Label `json:"added"`
+ Removed []Label `json:"removed"`
}
func (op *LabelChangeOperation) base() *OpBase {
return &op.OpBase
}
-func (op *LabelChangeOperation) ID() string {
+func (op *LabelChangeOperation) Id() entity.Id {
return idOperation(op)
}
@@ -61,7 +62,7 @@ AddLoop:
})
item := &LabelChangeTimelineItem{
- id: op.ID(),
+ id: op.Id(),
Author: op.Author,
UnixTime: timestamp.Timestamp(op.UnixTime),
Added: op.Added,
@@ -95,28 +96,9 @@ func (op *LabelChangeOperation) Validate() error {
return nil
}
-// Workaround to avoid the inner OpBase.MarshalJSON overriding the outer op
-// MarshalJSON
-func (op *LabelChangeOperation) MarshalJSON() ([]byte, error) {
- base, err := json.Marshal(op.OpBase)
- if err != nil {
- return nil, err
- }
-
- // revert back to a flat map to be able to add our own fields
- var data map[string]interface{}
- if err := json.Unmarshal(base, &data); err != nil {
- return nil, err
- }
-
- data["added"] = op.Added
- data["removed"] = op.Removed
-
- return json.Marshal(data)
-}
-
-// Workaround to avoid the inner OpBase.MarshalJSON overriding the outer op
-// MarshalJSON
+// UnmarshalJSON is a two step JSON unmarshaling
+// This workaround is necessary to avoid the inner OpBase.MarshalJSON
+// overriding the outer op's MarshalJSON
func (op *LabelChangeOperation) UnmarshalJSON(data []byte) error {
// Unmarshal OpBase and the op separately
@@ -155,14 +137,14 @@ func NewLabelChangeOperation(author identity.Interface, unixTime int64, added, r
}
type LabelChangeTimelineItem struct {
- id string
+ id entity.Id
Author identity.Interface
UnixTime timestamp.Timestamp
Added []Label
Removed []Label
}
-func (l LabelChangeTimelineItem) ID() string {
+func (l LabelChangeTimelineItem) Id() entity.Id {
return l.id
}
diff --git a/bug/op_label_change_test.go b/bug/op_label_change_test.go
index 7c311a8b..2a93e362 100644
--- a/bug/op_label_change_test.go
+++ b/bug/op_label_change_test.go
@@ -21,8 +21,9 @@ func TestLabelChangeSerialize(t *testing.T) {
err = json.Unmarshal(data, &after)
assert.NoError(t, err)
- // enforce creating the ID
- before.ID()
+ // enforce creating the IDs
+ before.Id()
+ rene.Id()
assert.Equal(t, before, &after)
}
diff --git a/bug/op_noop.go b/bug/op_noop.go
index a1648434..16d32297 100644
--- a/bug/op_noop.go
+++ b/bug/op_noop.go
@@ -3,6 +3,7 @@ package bug
import (
"encoding/json"
+ "github.com/MichaelMure/git-bug/entity"
"github.com/MichaelMure/git-bug/identity"
)
@@ -19,7 +20,7 @@ func (op *NoOpOperation) base() *OpBase {
return &op.OpBase
}
-func (op *NoOpOperation) ID() string {
+func (op *NoOpOperation) Id() entity.Id {
return idOperation(op)
}
@@ -31,25 +32,9 @@ func (op *NoOpOperation) Validate() error {
return opBaseValidate(op, NoOpOp)
}
-// Workaround to avoid the inner OpBase.MarshalJSON overriding the outer op
-// MarshalJSON
-func (op *NoOpOperation) MarshalJSON() ([]byte, error) {
- base, err := json.Marshal(op.OpBase)
- if err != nil {
- return nil, err
- }
-
- // revert back to a flat map to be able to add our own fields
- var data map[string]interface{}
- if err := json.Unmarshal(base, &data); err != nil {
- return nil, err
- }
-
- return json.Marshal(data)
-}
-
-// Workaround to avoid the inner OpBase.MarshalJSON overriding the outer op
-// MarshalJSON
+// UnmarshalJSON is a two step JSON unmarshaling
+// This workaround is necessary to avoid the inner OpBase.MarshalJSON
+// overriding the outer op's MarshalJSON
func (op *NoOpOperation) UnmarshalJSON(data []byte) error {
// Unmarshal OpBase and the op separately
diff --git a/bug/op_noop_test.go b/bug/op_noop_test.go
index 33e37bec..ea815948 100644
--- a/bug/op_noop_test.go
+++ b/bug/op_noop_test.go
@@ -21,8 +21,9 @@ func TestNoopSerialize(t *testing.T) {
err = json.Unmarshal(data, &after)
assert.NoError(t, err)
- // enforce creating the ID
- before.ID()
+ // enforce creating the IDs
+ before.Id()
+ rene.Id()
assert.Equal(t, before, &after)
}
diff --git a/bug/op_set_metadata.go b/bug/op_set_metadata.go
index 19ddc3e3..f99f836b 100644
--- a/bug/op_set_metadata.go
+++ b/bug/op_set_metadata.go
@@ -2,8 +2,10 @@ package bug
import (
"encoding/json"
- "fmt"
+ "github.com/pkg/errors"
+
+ "github.com/MichaelMure/git-bug/entity"
"github.com/MichaelMure/git-bug/identity"
)
@@ -11,21 +13,21 @@ var _ Operation = &SetMetadataOperation{}
type SetMetadataOperation struct {
OpBase
- Target string
- NewMetadata map[string]string
+ Target entity.Id `json:"target"`
+ NewMetadata map[string]string `json:"new_metadata"`
}
func (op *SetMetadataOperation) base() *OpBase {
return &op.OpBase
}
-func (op *SetMetadataOperation) ID() string {
+func (op *SetMetadataOperation) Id() entity.Id {
return idOperation(op)
}
func (op *SetMetadataOperation) Apply(snapshot *Snapshot) {
for _, target := range snapshot.Operations {
- if target.ID() == op.Target {
+ if target.Id() == op.Target {
base := target.base()
if base.extraMetadata == nil {
@@ -48,35 +50,16 @@ func (op *SetMetadataOperation) Validate() error {
return err
}
- if !IDIsValid(op.Target) {
- return fmt.Errorf("target hash is invalid")
+ if err := op.Target.Validate(); err != nil {
+ return errors.Wrap(err, "target invalid")
}
return nil
}
-// Workaround to avoid the inner OpBase.MarshalJSON overriding the outer op
-// MarshalJSON
-func (op *SetMetadataOperation) MarshalJSON() ([]byte, error) {
- base, err := json.Marshal(op.OpBase)
- if err != nil {
- return nil, err
- }
-
- // revert back to a flat map to be able to add our own fields
- var data map[string]interface{}
- if err := json.Unmarshal(base, &data); err != nil {
- return nil, err
- }
-
- data["target"] = op.Target
- data["new_metadata"] = op.NewMetadata
-
- return json.Marshal(data)
-}
-
-// Workaround to avoid the inner OpBase.MarshalJSON overriding the outer op
-// MarshalJSON
+// UnmarshalJSON is a two step JSON unmarshaling
+// This workaround is necessary to avoid the inner OpBase.MarshalJSON
+// overriding the outer op's MarshalJSON
func (op *SetMetadataOperation) UnmarshalJSON(data []byte) error {
// Unmarshal OpBase and the op separately
@@ -87,7 +70,7 @@ func (op *SetMetadataOperation) UnmarshalJSON(data []byte) error {
}
aux := struct {
- Target string `json:"target"`
+ Target entity.Id `json:"target"`
NewMetadata map[string]string `json:"new_metadata"`
}{}
@@ -106,7 +89,7 @@ func (op *SetMetadataOperation) UnmarshalJSON(data []byte) error {
// Sign post method for gqlgen
func (op *SetMetadataOperation) IsAuthored() {}
-func NewSetMetadataOp(author identity.Interface, unixTime int64, target string, newMetadata map[string]string) *SetMetadataOperation {
+func NewSetMetadataOp(author identity.Interface, unixTime int64, target entity.Id, newMetadata map[string]string) *SetMetadataOperation {
return &SetMetadataOperation{
OpBase: newOpBase(SetMetadataOp, author, unixTime),
Target: target,
@@ -115,7 +98,7 @@ func NewSetMetadataOp(author identity.Interface, unixTime int64, target string,
}
// Convenience function to apply the operation
-func SetMetadata(b Interface, author identity.Interface, unixTime int64, target string, newMetadata map[string]string) (*SetMetadataOperation, error) {
+func SetMetadata(b Interface, author identity.Interface, unixTime int64, target entity.Id, newMetadata map[string]string) (*SetMetadataOperation, error) {
SetMetadataOp := NewSetMetadataOp(author, unixTime, target, newMetadata)
if err := SetMetadataOp.Validate(); err != nil {
return nil, err
diff --git a/bug/op_set_metadata_test.go b/bug/op_set_metadata_test.go
index 97e70cf4..389e91ac 100644
--- a/bug/op_set_metadata_test.go
+++ b/bug/op_set_metadata_test.go
@@ -21,18 +21,18 @@ func TestSetMetadata(t *testing.T) {
create.Apply(&snapshot)
snapshot.Operations = append(snapshot.Operations, create)
- hash1 := create.ID()
- require.True(t, IDIsValid(hash1))
+ id1 := create.Id()
+ require.NoError(t, id1.Validate())
comment := NewAddCommentOp(rene, unix, "comment", nil)
comment.SetMetadata("key2", "value2")
comment.Apply(&snapshot)
snapshot.Operations = append(snapshot.Operations, comment)
- hash2 := comment.ID()
- require.True(t, IDIsValid(hash2))
+ id2 := comment.Id()
+ require.NoError(t, id2.Validate())
- op1 := NewSetMetadataOp(rene, unix, hash1, map[string]string{
+ op1 := NewSetMetadataOp(rene, unix, id1, map[string]string{
"key": "override",
"key2": "value",
})
@@ -51,7 +51,7 @@ func TestSetMetadata(t *testing.T) {
assert.Equal(t, len(commentMetadata), 1)
assert.Equal(t, commentMetadata["key2"], "value2")
- op2 := NewSetMetadataOp(rene, unix, hash2, map[string]string{
+ op2 := NewSetMetadataOp(rene, unix, id2, map[string]string{
"key2": "value",
"key3": "value3",
})
@@ -71,7 +71,7 @@ func TestSetMetadata(t *testing.T) {
// new key is set
assert.Equal(t, commentMetadata["key3"], "value3")
- op3 := NewSetMetadataOp(rene, unix, hash1, map[string]string{
+ op3 := NewSetMetadataOp(rene, unix, id1, map[string]string{
"key": "override",
"key2": "override",
})
@@ -107,8 +107,9 @@ func TestSetMetadataSerialize(t *testing.T) {
err = json.Unmarshal(data, &after)
assert.NoError(t, err)
- // enforce creating the ID
- before.ID()
+ // enforce creating the IDs
+ before.Id()
+ rene.Id()
assert.Equal(t, before, &after)
}
diff --git a/bug/op_set_status.go b/bug/op_set_status.go
index 76117b19..8a245184 100644
--- a/bug/op_set_status.go
+++ b/bug/op_set_status.go
@@ -5,6 +5,7 @@ import (
"github.com/pkg/errors"
+ "github.com/MichaelMure/git-bug/entity"
"github.com/MichaelMure/git-bug/identity"
"github.com/MichaelMure/git-bug/util/timestamp"
)
@@ -14,14 +15,14 @@ var _ Operation = &SetStatusOperation{}
// SetStatusOperation will change the status of a bug
type SetStatusOperation struct {
OpBase
- Status Status
+ Status Status `json:"status"`
}
func (op *SetStatusOperation) base() *OpBase {
return &op.OpBase
}
-func (op *SetStatusOperation) ID() string {
+func (op *SetStatusOperation) Id() entity.Id {
return idOperation(op)
}
@@ -30,7 +31,7 @@ func (op *SetStatusOperation) Apply(snapshot *Snapshot) {
snapshot.addActor(op.Author)
item := &SetStatusTimelineItem{
- id: op.ID(),
+ id: op.Id(),
Author: op.Author,
UnixTime: timestamp.Timestamp(op.UnixTime),
Status: op.Status,
@@ -51,27 +52,9 @@ func (op *SetStatusOperation) Validate() error {
return nil
}
-// Workaround to avoid the inner OpBase.MarshalJSON overriding the outer op
-// MarshalJSON
-func (op *SetStatusOperation) MarshalJSON() ([]byte, error) {
- base, err := json.Marshal(op.OpBase)
- if err != nil {
- return nil, err
- }
-
- // revert back to a flat map to be able to add our own fields
- var data map[string]interface{}
- if err := json.Unmarshal(base, &data); err != nil {
- return nil, err
- }
-
- data["status"] = op.Status
-
- return json.Marshal(data)
-}
-
-// Workaround to avoid the inner OpBase.MarshalJSON overriding the outer op
-// MarshalJSON
+// UnmarshalJSON is a two step JSON unmarshaling
+// This workaround is necessary to avoid the inner OpBase.MarshalJSON
+// overriding the outer op's MarshalJSON
func (op *SetStatusOperation) UnmarshalJSON(data []byte) error {
// Unmarshal OpBase and the op separately
@@ -107,13 +90,13 @@ func NewSetStatusOp(author identity.Interface, unixTime int64, status Status) *S
}
type SetStatusTimelineItem struct {
- id string
+ id entity.Id
Author identity.Interface
UnixTime timestamp.Timestamp
Status Status
}
-func (s SetStatusTimelineItem) ID() string {
+func (s SetStatusTimelineItem) Id() entity.Id {
return s.id
}
diff --git a/bug/op_set_status_test.go b/bug/op_set_status_test.go
index fc999234..ea032184 100644
--- a/bug/op_set_status_test.go
+++ b/bug/op_set_status_test.go
@@ -21,8 +21,9 @@ func TestSetStatusSerialize(t *testing.T) {
err = json.Unmarshal(data, &after)
assert.NoError(t, err)
- // enforce creating the ID
- before.ID()
+ // enforce creating the IDs
+ before.Id()
+ rene.Id()
assert.Equal(t, before, &after)
}
diff --git a/bug/op_set_title.go b/bug/op_set_title.go
index 7bebfadc..fadd29a9 100644
--- a/bug/op_set_title.go
+++ b/bug/op_set_title.go
@@ -5,6 +5,7 @@ import (
"fmt"
"strings"
+ "github.com/MichaelMure/git-bug/entity"
"github.com/MichaelMure/git-bug/identity"
"github.com/MichaelMure/git-bug/util/timestamp"
@@ -16,15 +17,15 @@ var _ Operation = &SetTitleOperation{}
// SetTitleOperation will change the title of a bug
type SetTitleOperation struct {
OpBase
- Title string
- Was string
+ Title string `json:"title"`
+ Was string `json:"was"`
}
func (op *SetTitleOperation) base() *OpBase {
return &op.OpBase
}
-func (op *SetTitleOperation) ID() string {
+func (op *SetTitleOperation) Id() entity.Id {
return idOperation(op)
}
@@ -33,7 +34,7 @@ func (op *SetTitleOperation) Apply(snapshot *Snapshot) {
snapshot.addActor(op.Author)
item := &SetTitleTimelineItem{
- id: op.ID(),
+ id: op.Id(),
Author: op.Author,
UnixTime: timestamp.Timestamp(op.UnixTime),
Title: op.Title,
@@ -71,28 +72,9 @@ func (op *SetTitleOperation) Validate() error {
return nil
}
-// Workaround to avoid the inner OpBase.MarshalJSON overriding the outer op
-// MarshalJSON
-func (op *SetTitleOperation) MarshalJSON() ([]byte, error) {
- base, err := json.Marshal(op.OpBase)
- if err != nil {
- return nil, err
- }
-
- // revert back to a flat map to be able to add our own fields
- var data map[string]interface{}
- if err := json.Unmarshal(base, &data); err != nil {
- return nil, err
- }
-
- data["title"] = op.Title
- data["was"] = op.Was
-
- return json.Marshal(data)
-}
-
-// Workaround to avoid the inner OpBase.MarshalJSON overriding the outer op
-// MarshalJSON
+// UnmarshalJSON is a two step JSON unmarshaling
+// This workaround is necessary to avoid the inner OpBase.MarshalJSON
+// overriding the outer op's MarshalJSON
func (op *SetTitleOperation) UnmarshalJSON(data []byte) error {
// Unmarshal OpBase and the op separately
@@ -131,14 +113,14 @@ func NewSetTitleOp(author identity.Interface, unixTime int64, title string, was
}
type SetTitleTimelineItem struct {
- id string
+ id entity.Id
Author identity.Interface
UnixTime timestamp.Timestamp
Title string
Was string
}
-func (s SetTitleTimelineItem) ID() string {
+func (s SetTitleTimelineItem) Id() entity.Id {
return s.id
}
diff --git a/bug/op_set_title_test.go b/bug/op_set_title_test.go
index 8ffe4f3c..19cbb12b 100644
--- a/bug/op_set_title_test.go
+++ b/bug/op_set_title_test.go
@@ -21,8 +21,9 @@ func TestSetTitleSerialize(t *testing.T) {
err = json.Unmarshal(data, &after)
assert.NoError(t, err)
- // enforce creating the ID
- before.ID()
+ // enforce creating the IDs
+ before.Id()
+ rene.Id()
assert.Equal(t, before, &after)
}
diff --git a/bug/operation.go b/bug/operation.go
index 2298a008..dd95e096 100644
--- a/bug/operation.go
+++ b/bug/operation.go
@@ -6,10 +6,11 @@ import (
"fmt"
"time"
- "github.com/MichaelMure/git-bug/identity"
+ "github.com/pkg/errors"
+ "github.com/MichaelMure/git-bug/entity"
+ "github.com/MichaelMure/git-bug/identity"
"github.com/MichaelMure/git-bug/util/git"
- "github.com/pkg/errors"
)
// OperationType is an operation type identifier
@@ -27,14 +28,12 @@ const (
SetMetadataOp
)
-const unsetIDMarker = "unset"
-
// Operation define the interface to fulfill for an edit operation of a Bug
type Operation interface {
// base return the OpBase of the Operation, for package internal use
base() *OpBase
- // ID return the identifier of the operation, to be used for back references
- ID() string
+ // Id return the identifier of the operation, to be used for back references
+ Id() entity.Id
// Time return the time when the operation was added
Time() time.Time
// GetUnixTime return the unix timestamp when the operation was added
@@ -55,57 +54,42 @@ type Operation interface {
GetAuthor() identity.Interface
}
-func hashRaw(data []byte) string {
- hasher := sha256.New()
- // Write can't fail
- _, _ = hasher.Write(data)
- return fmt.Sprintf("%x", hasher.Sum(nil))
+func deriveId(data []byte) entity.Id {
+ sum := sha256.Sum256(data)
+ return entity.Id(fmt.Sprintf("%x", sum))
}
-func idOperation(op Operation) string {
+func idOperation(op Operation) entity.Id {
base := op.base()
if base.id == "" {
// something went really wrong
panic("op's id not set")
}
- if base.id == "unset" {
- // This means we are trying to get the op's ID *before* it has been stored, for instance when
+ if base.id == entity.UnsetId {
+ // This means we are trying to get the op's Id *before* it has been stored, for instance when
// adding multiple ops in one go in an OperationPack.
- // As the ID is computed based on the actual bytes written on the disk, we are going to predict
- // those and then get the ID. This is safe as it will be the exact same code writing on disk later.
+ // As the Id is computed based on the actual bytes written on the disk, we are going to predict
+ // those and then get the Id. This is safe as it will be the exact same code writing on disk later.
data, err := json.Marshal(op)
if err != nil {
panic(err)
}
- base.id = hashRaw(data)
+ base.id = deriveId(data)
}
return base.id
}
-func IDIsValid(id string) bool {
- // IDs have the same format as a git hash
- if len(id) != 40 && len(id) != 64 {
- return false
- }
- for _, r := range id {
- if (r < 'a' || r > 'z') && (r < '0' || r > '9') {
- return false
- }
- }
- return true
-}
-
// OpBase implement the common code for all operations
type OpBase struct {
- OperationType OperationType
- Author identity.Interface
- UnixTime int64
- Metadata map[string]string
+ OperationType OperationType `json:"type"`
+ Author identity.Interface `json:"author"`
+ UnixTime int64 `json:"timestamp"`
+ Metadata map[string]string `json:"metadata,omitempty"`
// Not serialized. Store the op's id in memory.
- id string
+ id entity.Id
// Not serialized. Store the extra metadata in memory,
// compiled from SetMetadataOperation.
extraMetadata map[string]string
@@ -117,27 +101,13 @@ func newOpBase(opType OperationType, author identity.Interface, unixTime int64)
OperationType: opType,
Author: author,
UnixTime: unixTime,
- id: unsetIDMarker,
+ id: entity.UnsetId,
}
}
-func (op OpBase) MarshalJSON() ([]byte, error) {
- return json.Marshal(struct {
- OperationType OperationType `json:"type"`
- Author identity.Interface `json:"author"`
- UnixTime int64 `json:"timestamp"`
- Metadata map[string]string `json:"metadata,omitempty"`
- }{
- OperationType: op.OperationType,
- Author: op.Author,
- UnixTime: op.UnixTime,
- Metadata: op.Metadata,
- })
-}
-
func (op *OpBase) UnmarshalJSON(data []byte) error {
- // Compute the ID when loading the op from disk.
- op.id = hashRaw(data)
+ // Compute the Id when loading the op from disk.
+ op.id = deriveId(data)
aux := struct {
OperationType OperationType `json:"type"`
@@ -213,7 +183,7 @@ func (op *OpBase) SetMetadata(key string, value string) {
}
op.Metadata[key] = value
- op.id = unsetIDMarker
+ op.id = entity.UnsetId
}
// GetMetadata retrieve arbitrary metadata about the operation
diff --git a/bug/operation_pack_test.go b/bug/operation_pack_test.go
index 713be7f0..21ac0a00 100644
--- a/bug/operation_pack_test.go
+++ b/bug/operation_pack_test.go
@@ -48,14 +48,16 @@ func TestOperationPackSerialize(t *testing.T) {
err = json.Unmarshal(data, &opp2)
assert.NoError(t, err)
- ensureID(t, opp)
+ ensureIDs(t, opp)
assert.Equal(t, opp, opp2)
}
-func ensureID(t *testing.T, opp *OperationPack) {
+func ensureIDs(t *testing.T, opp *OperationPack) {
for _, op := range opp.Operations {
- id := op.ID()
- require.True(t, IDIsValid(id))
+ id := op.Id()
+ require.NoError(t, id.Validate())
+ id = op.GetAuthor().Id()
+ require.NoError(t, id.Validate())
}
}
diff --git a/bug/operation_test.go b/bug/operation_test.go
index c8dc246e..69c66dc8 100644
--- a/bug/operation_test.go
+++ b/bug/operation_test.go
@@ -94,26 +94,26 @@ func TestID(t *testing.T) {
b, op, err := Create(rene, time.Now().Unix(), "title", "message")
require.Nil(t, err)
- id1 := op.ID()
- require.True(t, IDIsValid(id1))
+ id1 := op.Id()
+ require.NoError(t, id1.Validate())
err = b.Commit(repo)
require.Nil(t, err)
op2 := b.FirstOp()
- id2 := op2.ID()
- require.True(t, IDIsValid(id2))
+ id2 := op2.Id()
+ require.NoError(t, id2.Validate())
require.Equal(t, id1, id2)
- b2, err := ReadLocalBug(repo, b.id)
+ b2, err := ReadLocalBug(repo, b.Id())
require.Nil(t, err)
op3 := b2.FirstOp()
- id3 := op3.ID()
- require.True(t, IDIsValid(id3))
+ id3 := op3.Id()
+ require.NoError(t, id3.Validate())
require.Equal(t, id1, id3)
}
diff --git a/bug/snapshot.go b/bug/snapshot.go
index b2e6f130..a937200f 100644
--- a/bug/snapshot.go
+++ b/bug/snapshot.go
@@ -4,12 +4,13 @@ import (
"fmt"
"time"
+ "github.com/MichaelMure/git-bug/entity"
"github.com/MichaelMure/git-bug/identity"
)
// Snapshot is a compiled form of the Bug data structure used for storage and merge
type Snapshot struct {
- id string
+ id entity.Id
Status Status
Title string
@@ -26,15 +27,10 @@ type Snapshot struct {
}
// Return the Bug identifier
-func (snap *Snapshot) Id() string {
+func (snap *Snapshot) Id() entity.Id {
return snap.id
}
-// Return the Bug identifier truncated for human consumption
-func (snap *Snapshot) HumanId() string {
- return FormatHumanID(snap.id)
-}
-
// Return the last time a bug was modified
func (snap *Snapshot) LastEditTime() time.Time {
if len(snap.Operations) == 0 {
@@ -59,9 +55,9 @@ func (snap *Snapshot) GetCreateMetadata(key string) (string, bool) {
}
// SearchTimelineItem will search in the timeline for an item matching the given hash
-func (snap *Snapshot) SearchTimelineItem(ID string) (TimelineItem, error) {
+func (snap *Snapshot) SearchTimelineItem(id entity.Id) (TimelineItem, error) {
for i := range snap.Timeline {
- if snap.Timeline[i].ID() == ID {
+ if snap.Timeline[i].Id() == id {
return snap.Timeline[i], nil
}
}
@@ -72,7 +68,7 @@ func (snap *Snapshot) SearchTimelineItem(ID string) (TimelineItem, error) {
// SearchComment will search for a comment matching the given hash
func (snap *Snapshot) SearchComment(id string) (*Comment, error) {
for _, c := range snap.Comments {
- if c.id == id {
+ if c.id.String() == id {
return &c, nil
}
}
@@ -103,7 +99,7 @@ func (snap *Snapshot) addParticipant(participant identity.Interface) {
}
// HasParticipant return true if the id is a participant
-func (snap *Snapshot) HasParticipant(id string) bool {
+func (snap *Snapshot) HasParticipant(id entity.Id) bool {
for _, p := range snap.Participants {
if p.Id() == id {
return true
@@ -113,7 +109,7 @@ func (snap *Snapshot) HasParticipant(id string) bool {
}
// HasAnyParticipant return true if one of the ids is a participant
-func (snap *Snapshot) HasAnyParticipant(ids ...string) bool {
+func (snap *Snapshot) HasAnyParticipant(ids ...entity.Id) bool {
for _, id := range ids {
if snap.HasParticipant(id) {
return true
@@ -123,7 +119,7 @@ func (snap *Snapshot) HasAnyParticipant(ids ...string) bool {
}
// HasActor return true if the id is a actor
-func (snap *Snapshot) HasActor(id string) bool {
+func (snap *Snapshot) HasActor(id entity.Id) bool {
for _, p := range snap.Actors {
if p.Id() == id {
return true
@@ -133,7 +129,7 @@ func (snap *Snapshot) HasActor(id string) bool {
}
// HasAnyActor return true if one of the ids is a actor
-func (snap *Snapshot) HasAnyActor(ids ...string) bool {
+func (snap *Snapshot) HasAnyActor(ids ...entity.Id) bool {
for _, id := range ids {
if snap.HasActor(id) {
return true
diff --git a/bug/timeline.go b/bug/timeline.go
index 247c2927..4af1b92a 100644
--- a/bug/timeline.go
+++ b/bug/timeline.go
@@ -3,6 +3,7 @@ package bug
import (
"strings"
+ "github.com/MichaelMure/git-bug/entity"
"github.com/MichaelMure/git-bug/identity"
"github.com/MichaelMure/git-bug/util/git"
"github.com/MichaelMure/git-bug/util/timestamp"
@@ -10,7 +11,7 @@ import (
type TimelineItem interface {
// ID return the identifier of the item
- ID() string
+ Id() entity.Id
}
// CommentHistoryStep hold one version of a message in the history
@@ -25,7 +26,7 @@ type CommentHistoryStep struct {
// CommentTimelineItem is a TimelineItem that holds a Comment and its edition history
type CommentTimelineItem struct {
- id string
+ id entity.Id
Author identity.Interface
Message string
Files []git.Hash
@@ -34,7 +35,7 @@ type CommentTimelineItem struct {
History []CommentHistoryStep
}
-func NewCommentTimelineItem(ID string, comment Comment) CommentTimelineItem {
+func NewCommentTimelineItem(ID entity.Id, comment Comment) CommentTimelineItem {
return CommentTimelineItem{
id: ID,
Author: comment.Author,
@@ -51,7 +52,7 @@ func NewCommentTimelineItem(ID string, comment Comment) CommentTimelineItem {
}
}
-func (c *CommentTimelineItem) ID() string {
+func (c *CommentTimelineItem) Id() entity.Id {
return c.id
}