aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAmine <hilalyamine@gmail.com>2019-08-13 16:47:24 +0200
committerGitHub <noreply@github.com>2019-08-13 16:47:24 +0200
commitcf960bc7a5bd0b7af28d35de33131fb0b5ce5253 (patch)
tree5df133c91bb4e1ccc5f9fbeb4664416b93d23bf5
parent146894a5657d3b20dbaf769a950b12bd19df499c (diff)
parentc809d37152ea87a66fc281730042dcb4299a8263 (diff)
downloadgit-bug-cf960bc7a5bd0b7af28d35de33131fb0b5ce5253.tar.gz
Merge pull request #193 from MichaelMure/immutableID
Future proof the operation's ID
-rw-r--r--bridge/core/export.go28
-rw-r--r--bridge/github/export.go80
-rw-r--r--bridge/github/export_test.go10
-rw-r--r--bridge/github/import.go41
-rw-r--r--bridge/gitlab/import.go25
-rw-r--r--bridge/launchpad/import.go7
-rw-r--r--bug/bug.go59
-rw-r--r--bug/bug_actions.go11
-rw-r--r--bug/bug_test.go2
-rw-r--r--bug/comment.go10
-rw-r--r--bug/interface.go6
-rw-r--r--bug/op_add_comment.go45
-rw-r--r--bug/op_add_comment_test.go7
-rw-r--r--bug/op_create.go48
-rw-r--r--bug/op_create_test.go12
-rw-r--r--bug/op_edit_comment.go59
-rw-r--r--bug/op_edit_comment_test.go22
-rw-r--r--bug/op_label_change.go54
-rw-r--r--bug/op_label_change_test.go4
-rw-r--r--bug/op_noop.go28
-rw-r--r--bug/op_noop_test.go4
-rw-r--r--bug/op_set_metadata.go57
-rw-r--r--bug/op_set_metadata_test.go18
-rw-r--r--bug/op_set_status.go50
-rw-r--r--bug/op_set_status_test.go4
-rw-r--r--bug/op_set_title.go50
-rw-r--r--bug/op_set_title_test.go4
-rw-r--r--bug/operation.go79
-rw-r--r--bug/operation_pack.go3
-rw-r--r--bug/operation_pack_test.go10
-rw-r--r--bug/operation_test.go20
-rw-r--r--bug/snapshot.go27
-rw-r--r--bug/timeline.go15
-rw-r--r--cache/bug_cache.go46
-rw-r--r--cache/bug_excerpt.go17
-rw-r--r--cache/identity_excerpt.go9
-rw-r--r--cache/repo_cache.go74
-rw-r--r--cache/repo_cache_test.go8
-rw-r--r--commands/add.go2
-rw-r--r--commands/comment.go2
-rw-r--r--commands/ls-id.go6
-rw-r--r--commands/ls.go2
-rw-r--r--commands/pull.go6
-rw-r--r--commands/select.go2
-rw-r--r--commands/select/select.go15
-rw-r--r--commands/select/select_test.go6
-rw-r--r--commands/show.go6
-rw-r--r--commands/user.go2
-rw-r--r--commands/user_ls.go2
-rw-r--r--entity/err.go27
-rw-r--r--entity/id.go67
-rw-r--r--entity/interface.go4
-rw-r--r--entity/merge.go12
-rw-r--r--graphql/connections/connections.go4
-rw-r--r--graphql/connections/gen_lazy_bug.go11
-rw-r--r--graphql/connections/gen_lazy_identity.go11
-rw-r--r--graphql/connections/lazy_bug.go4
-rw-r--r--graphql/connections/lazy_identity.go4
-rw-r--r--graphql/graph/gen_graph.go551
-rw-r--r--graphql/resolvers/bug.go8
-rw-r--r--graphql/resolvers/identity.go4
-rw-r--r--graphql/resolvers/operations.go45
-rw-r--r--graphql/resolvers/repo.go9
-rw-r--r--graphql/resolvers/root.go2
-rw-r--r--graphql/resolvers/timeline.go33
-rw-r--r--graphql/schema/operations.graphql30
-rw-r--r--graphql/schema/timeline.graphql24
-rw-r--r--identity/bare.go48
-rw-r--r--identity/bare_test.go7
-rw-r--r--identity/common.go17
-rw-r--r--identity/identity.go51
-rw-r--r--identity/identity_actions.go11
-rw-r--r--identity/identity_stub.go14
-rw-r--r--identity/identity_stub_test.go3
-rw-r--r--identity/identity_test.go7
-rw-r--r--identity/interface.go6
-rw-r--r--identity/resolver.go9
-rw-r--r--termui/bug_table.go6
-rw-r--r--termui/show_bug.go12
-rw-r--r--termui/termui.go9
-rw-r--r--util/git/hash.go8
81 files changed, 1134 insertions, 1028 deletions
diff --git a/bridge/core/export.go b/bridge/core/export.go
index 2bcf0087..09566b62 100644
--- a/bridge/core/export.go
+++ b/bridge/core/export.go
@@ -1,6 +1,10 @@
package core
-import "fmt"
+import (
+ "fmt"
+
+ "github.com/MichaelMure/git-bug/entity"
+)
type ExportEvent int
@@ -21,7 +25,7 @@ const (
type ExportResult struct {
Err error
Event ExportEvent
- ID string
+ ID entity.Id
Reason string
}
@@ -46,14 +50,14 @@ func (er ExportResult) String() string {
}
}
-func NewExportError(err error, reason string) ExportResult {
+func NewExportError(err error, id entity.Id) ExportResult {
return ExportResult{
- Err: err,
- Reason: reason,
+ ID: id,
+ Err: err,
}
}
-func NewExportNothing(id string, reason string) ExportResult {
+func NewExportNothing(id entity.Id, reason string) ExportResult {
return ExportResult{
ID: id,
Reason: reason,
@@ -61,42 +65,42 @@ func NewExportNothing(id string, reason string) ExportResult {
}
}
-func NewExportBug(id string) ExportResult {
+func NewExportBug(id entity.Id) ExportResult {
return ExportResult{
ID: id,
Event: ExportEventBug,
}
}
-func NewExportComment(id string) ExportResult {
+func NewExportComment(id entity.Id) ExportResult {
return ExportResult{
ID: id,
Event: ExportEventComment,
}
}
-func NewExportCommentEdition(id string) ExportResult {
+func NewExportCommentEdition(id entity.Id) ExportResult {
return ExportResult{
ID: id,
Event: ExportEventCommentEdition,
}
}
-func NewExportStatusChange(id string) ExportResult {
+func NewExportStatusChange(id entity.Id) ExportResult {
return ExportResult{
ID: id,
Event: ExportEventStatusChange,
}
}
-func NewExportLabelChange(id string) ExportResult {
+func NewExportLabelChange(id entity.Id) ExportResult {
return ExportResult{
ID: id,
Event: ExportEventLabelChange,
}
}
-func NewExportTitleEdition(id string) ExportResult {
+func NewExportTitleEdition(id entity.Id) ExportResult {
return ExportResult{
ID: id,
Event: ExportEventTitleEdition,
diff --git a/bridge/github/export.go b/bridge/github/export.go
index b4351bdb..34c88310 100644
--- a/bridge/github/export.go
+++ b/bridge/github/export.go
@@ -17,7 +17,7 @@ import (
"github.com/MichaelMure/git-bug/bridge/core"
"github.com/MichaelMure/git-bug/bug"
"github.com/MichaelMure/git-bug/cache"
- "github.com/MichaelMure/git-bug/util/git"
+ "github.com/MichaelMure/git-bug/entity"
)
var (
@@ -29,17 +29,17 @@ type githubExporter struct {
conf core.Configuration
// cache identities clients
- identityClient map[string]*githubv4.Client
+ identityClient map[entity.Id]*githubv4.Client
// map identities with their tokens
- identityToken map[string]string
+ identityToken map[entity.Id]string
// github repository ID
repositoryID string
// cache identifiers used to speed up exporting operations
// cleared for each bug
- cachedOperationIDs map[string]string
+ cachedOperationIDs map[entity.Id]string
// cache labels used to speed up exporting labels events
cachedLabels map[string]string
@@ -49,16 +49,16 @@ type githubExporter struct {
func (ge *githubExporter) Init(conf core.Configuration) error {
ge.conf = conf
//TODO: initialize with multiple tokens
- ge.identityToken = make(map[string]string)
- ge.identityClient = make(map[string]*githubv4.Client)
- ge.cachedOperationIDs = make(map[string]string)
+ 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)
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 string) (*githubv4.Client, error) {
+func (ge *githubExporter) getIdentityClient(id entity.Id) (*githubv4.Client, error) {
client, ok := ge.identityClient[id]
if ok {
return client, nil
@@ -103,7 +103,7 @@ func (ge *githubExporter) ExportAll(repo *cache.RepoCache, since time.Time) (<-c
go func() {
defer close(out)
- var allIdentitiesIds []string
+ var allIdentitiesIds []entity.Id
for id := range ge.identityToken {
allIdentitiesIds = append(allIdentitiesIds, id)
}
@@ -113,7 +113,7 @@ func (ge *githubExporter) ExportAll(repo *cache.RepoCache, since time.Time) (<-c
for _, id := range allBugsIds {
b, err := repo.ResolveBug(id)
if err != nil {
- out <- core.NewExportError(err, id)
+ out <- core.NewExportError(errors.Wrap(err, "can't load bug"), id)
return
}
@@ -144,7 +144,6 @@ func (ge *githubExporter) exportBug(b *cache.BugCache, since time.Time, out chan
var bugGithubID string
var bugGithubURL string
- var bugCreationHash string
// Special case:
// if a user try to export a bug that is not already exported to Github (or imported
@@ -167,7 +166,7 @@ func (ge *githubExporter) exportBug(b *cache.BugCache, since time.Time, out chan
githubURL, ok := snapshot.GetCreateMetadata(keyGithubUrl)
if !ok {
// if we find github ID, github URL must be found too
- err := fmt.Errorf("expected to find github issue URL")
+ err := fmt.Errorf("incomplete Github metadata: expected to find issue URL")
out <- core.NewExportError(err, b.Id())
}
@@ -209,15 +208,8 @@ func (ge *githubExporter) exportBug(b *cache.BugCache, since time.Time, out chan
out <- core.NewExportBug(b.Id())
- hash, err := createOp.Hash()
- if err != nil {
- err := errors.Wrap(err, "comment hash")
- out <- core.NewExportError(err, b.Id())
- return
- }
-
// mark bug creation operation as exported
- if err := markOperationAsExported(b, hash, id, url); err != nil {
+ if err := markOperationAsExported(b, createOp.Id(), id, url); err != nil {
err := errors.Wrap(err, "marking operation as exported")
out <- core.NewExportError(err, b.Id())
return
@@ -235,17 +227,8 @@ func (ge *githubExporter) exportBug(b *cache.BugCache, since time.Time, out chan
bugGithubURL = url
}
- // get createOp hash
- hash, err := createOp.Hash()
- if err != nil {
- out <- core.NewExportError(err, b.Id())
- return
- }
-
- bugCreationHash = hash.String()
-
// cache operation github id
- ge.cachedOperationIDs[bugCreationHash] = bugGithubID
+ ge.cachedOperationIDs[createOp.Id()] = bugGithubID
for _, op := range snapshot.Operations[1:] {
// ignore SetMetadata operations
@@ -253,26 +236,18 @@ func (ge *githubExporter) exportBug(b *cache.BugCache, since time.Time, out chan
continue
}
- // get operation hash
- hash, err := op.Hash()
- if err != nil {
- err := errors.Wrap(err, "operation hash")
- out <- core.NewExportError(err, b.Id())
- return
- }
-
// ignore operations already existing in github (due to import or export)
// cache the ID of already exported or imported issues and events from Github
if id, ok := op.GetMetadata(keyGithubId); ok {
- ge.cachedOperationIDs[hash.String()] = id
- out <- core.NewExportNothing(hash.String(), "already exported operation")
+ ge.cachedOperationIDs[op.Id()] = id
+ out <- core.NewExportNothing(op.Id(), "already exported operation")
continue
}
opAuthor := op.GetAuthor()
client, err := ge.getIdentityClient(opAuthor.Id())
if err != nil {
- out <- core.NewExportNothing(hash.String(), "missing operation author token")
+ out <- core.NewExportNothing(op.Id(), "missing operation author token")
continue
}
@@ -289,18 +264,17 @@ func (ge *githubExporter) exportBug(b *cache.BugCache, since time.Time, out chan
return
}
- out <- core.NewExportComment(hash.String())
+ out <- core.NewExportComment(op.Id())
// cache comment id
- ge.cachedOperationIDs[hash.String()] = id
+ ge.cachedOperationIDs[op.Id()] = id
case *bug.EditCommentOperation:
opr := op.(*bug.EditCommentOperation)
- targetHash := opr.Target.String()
// Since github doesn't consider the issue body as a comment
- if targetHash == bugCreationHash {
+ if opr.Target == createOp.Id() {
// case bug creation operation: we need to edit the Github issue
if err := updateGithubIssueBody(client, bugGithubID, opr.Message); err != nil {
@@ -309,7 +283,7 @@ func (ge *githubExporter) exportBug(b *cache.BugCache, since time.Time, out chan
return
}
- out <- core.NewExportCommentEdition(hash.String())
+ out <- core.NewExportCommentEdition(op.Id())
id = bugGithubID
url = bugGithubURL
@@ -317,7 +291,7 @@ func (ge *githubExporter) exportBug(b *cache.BugCache, since time.Time, out chan
} else {
// case comment edition operation: we need to edit the Github comment
- commentID, ok := ge.cachedOperationIDs[targetHash]
+ commentID, ok := ge.cachedOperationIDs[opr.Target]
if !ok {
panic("unexpected error: comment id not found")
}
@@ -329,7 +303,7 @@ func (ge *githubExporter) exportBug(b *cache.BugCache, since time.Time, out chan
return
}
- out <- core.NewExportCommentEdition(hash.String())
+ out <- core.NewExportCommentEdition(op.Id())
// use comment id/url instead of issue id/url
id = eid
@@ -344,7 +318,7 @@ func (ge *githubExporter) exportBug(b *cache.BugCache, since time.Time, out chan
return
}
- out <- core.NewExportStatusChange(hash.String())
+ out <- core.NewExportStatusChange(op.Id())
id = bugGithubID
url = bugGithubURL
@@ -357,7 +331,7 @@ func (ge *githubExporter) exportBug(b *cache.BugCache, since time.Time, out chan
return
}
- out <- core.NewExportTitleEdition(hash.String())
+ out <- core.NewExportTitleEdition(op.Id())
id = bugGithubID
url = bugGithubURL
@@ -370,7 +344,7 @@ func (ge *githubExporter) exportBug(b *cache.BugCache, since time.Time, out chan
return
}
- out <- core.NewExportLabelChange(hash.String())
+ out <- core.NewExportLabelChange(op.Id())
id = bugGithubID
url = bugGithubURL
@@ -380,7 +354,7 @@ func (ge *githubExporter) exportBug(b *cache.BugCache, since time.Time, out chan
}
// mark operation as exported
- if err := markOperationAsExported(b, hash, id, url); err != nil {
+ if err := markOperationAsExported(b, op.Id(), id, url); err != nil {
err := errors.Wrap(err, "marking operation as exported")
out <- core.NewExportError(err, b.Id())
return
@@ -438,7 +412,7 @@ func getRepositoryNodeID(owner, project, token string) (string, error) {
return aux.NodeID, nil
}
-func markOperationAsExported(b *cache.BugCache, target git.Hash, githubID, githubURL string) error {
+func markOperationAsExported(b *cache.BugCache, target entity.Id, githubID, githubURL string) error {
_, err := b.SetMetadata(
target,
map[string]string{
diff --git a/bridge/github/export_test.go b/bridge/github/export_test.go
index 80660e77..107fe63b 100644
--- a/bridge/github/export_test.go
+++ b/bridge/github/export_test.go
@@ -58,19 +58,13 @@ func testCases(t *testing.T, repo *cache.RepoCache, identity *cache.IdentityCach
bugWithCommentEditions, createOp, err := repo.NewBug("bug with comments editions", "new bug")
require.NoError(t, err)
- createOpHash, err := createOp.Hash()
- require.NoError(t, err)
-
- _, err = bugWithCommentEditions.EditComment(createOpHash, "first comment edited")
+ _, err = bugWithCommentEditions.EditComment(createOp.Id(), "first comment edited")
require.NoError(t, err)
commentOp, err := bugWithCommentEditions.AddComment("first comment")
require.NoError(t, err)
- commentOpHash, err := commentOp.Hash()
- require.NoError(t, err)
-
- _, err = bugWithCommentEditions.EditComment(commentOpHash, "first comment edited")
+ _, err = bugWithCommentEditions.EditComment(commentOp.Id(), "first comment edited")
require.NoError(t, err)
// bug status changed
diff --git a/bridge/github/import.go b/bridge/github/import.go
index c162d70e..dcaf2d05 100644
--- a/bridge/github/import.go
+++ b/bridge/github/import.go
@@ -10,8 +10,7 @@ import (
"github.com/MichaelMure/git-bug/bridge/core"
"github.com/MichaelMure/git-bug/bug"
"github.com/MichaelMure/git-bug/cache"
- "github.com/MichaelMure/git-bug/identity"
- "github.com/MichaelMure/git-bug/util/git"
+ "github.com/MichaelMure/git-bug/entity"
"github.com/MichaelMure/git-bug/util/text"
)
@@ -93,7 +92,7 @@ func (gi *githubImporter) ensureIssue(repo *cache.RepoCache, issue issueTimeline
}
// get issue edits
- issueEdits := []userContentEdit{}
+ var issueEdits []userContentEdit
for gi.iterator.NextIssueEdit() {
issueEdits = append(issueEdits, gi.iterator.IssueEditValue())
}
@@ -307,15 +306,14 @@ func (gi *githubImporter) ensureTimelineComment(repo *cache.RepoCache, b *cache.
return err
}
- var target git.Hash
- target, err = b.ResolveOperationWithMetadata(keyGithubId, parseId(item.Id))
+ targetOpID, err := b.ResolveOperationWithMetadata(keyGithubId, parseId(item.Id))
if err != nil && err != cache.ErrNoMatchingOp {
// real error
return err
}
+
// if no edits are given we create the comment
if len(edits) == 0 {
-
// if comment doesn't exist
if err == cache.ErrNoMatchingOp {
cleanText, err := text.Cleanup(string(item.Body))
@@ -324,7 +322,7 @@ func (gi *githubImporter) ensureTimelineComment(repo *cache.RepoCache, b *cache.
}
// add comment operation
- op, err := b.AddCommentRaw(
+ _, err = b.AddCommentRaw(
author,
item.CreatedAt.Unix(),
cleanText,
@@ -334,19 +332,11 @@ func (gi *githubImporter) ensureTimelineComment(repo *cache.RepoCache, b *cache.
keyGithubUrl: parseId(item.Url.String()),
},
)
- if err != nil {
- return err
- }
-
- // set hash
- target, err = op.Hash()
- if err != nil {
- return err
- }
+ return err
}
} else {
for i, edit := range edits {
- if i == 0 && target != "" {
+ if i == 0 && targetOpID != "" {
// The first edit in the github result is the comment creation itself, we already have that
continue
}
@@ -358,7 +348,7 @@ func (gi *githubImporter) ensureTimelineComment(repo *cache.RepoCache, b *cache.
}
// create comment when target is empty
- if target == "" {
+ if targetOpID == "" {
cleanText, err := text.Cleanup(string(*edit.Diff))
if err != nil {
return err
@@ -378,16 +368,13 @@ func (gi *githubImporter) ensureTimelineComment(repo *cache.RepoCache, b *cache.
return err
}
- // set hash
- target, err = op.Hash()
- if err != nil {
- return err
- }
+ // set target for the nexr edit now that the comment is created
+ targetOpID = op.Id()
continue
}
- err = gi.ensureCommentEdit(repo, b, target, edit)
+ err = gi.ensureCommentEdit(repo, b, targetOpID, edit)
if err != nil {
return err
}
@@ -396,7 +383,7 @@ func (gi *githubImporter) ensureTimelineComment(repo *cache.RepoCache, b *cache.
return nil
}
-func (gi *githubImporter) ensureCommentEdit(repo *cache.RepoCache, b *cache.BugCache, target git.Hash, edit userContentEdit) error {
+func (gi *githubImporter) ensureCommentEdit(repo *cache.RepoCache, b *cache.BugCache, target entity.Id, edit userContentEdit) error {
_, err := b.ResolveOperationWithMetadata(keyGithubId, parseId(edit.Id))
if err == nil {
// already imported
@@ -458,7 +445,7 @@ func (gi *githubImporter) ensurePerson(repo *cache.RepoCache, actor *actor) (*ca
if err == nil {
return i, nil
}
- if _, ok := err.(identity.ErrMultipleMatch); ok {
+ if _, ok := err.(entity.ErrMultipleMatch); ok {
return nil, err
}
@@ -501,7 +488,7 @@ func (gi *githubImporter) getGhost(repo *cache.RepoCache) (*cache.IdentityCache,
if err == nil {
return i, nil
}
- if _, ok := err.(identity.ErrMultipleMatch); ok {
+ if _, ok := err.(entity.ErrMultipleMatch); ok {
return nil, err
}
diff --git a/bridge/gitlab/import.go b/bridge/gitlab/import.go
index 360a585f..e135b8bc 100644
--- a/bridge/gitlab/import.go
+++ b/bridge/gitlab/import.go
@@ -10,8 +10,7 @@ import (
"github.com/MichaelMure/git-bug/bridge/core"
"github.com/MichaelMure/git-bug/bug"
"github.com/MichaelMure/git-bug/cache"
- "github.com/MichaelMure/git-bug/identity"
- "github.com/MichaelMure/git-bug/util/git"
+ "github.com/MichaelMure/git-bug/entity"
"github.com/MichaelMure/git-bug/util/text"
)
@@ -130,9 +129,9 @@ func (gi *gitlabImporter) ensureIssue(repo *cache.RepoCache, issue *gitlab.Issue
}
func (gi *gitlabImporter) ensureNote(repo *cache.RepoCache, b *cache.BugCache, note *gitlab.Note) error {
- id := parseID(note.ID)
+ gitlabID := parseID(note.ID)
- hash, errResolve := b.ResolveOperationWithMetadata(keyGitlabId, id)
+ id, errResolve := b.ResolveOperationWithMetadata(keyGitlabId, gitlabID)
if errResolve != nil && errResolve != cache.ErrNoMatchingOp {
return errResolve
}
@@ -154,7 +153,7 @@ func (gi *gitlabImporter) ensureNote(repo *cache.RepoCache, b *cache.BugCache, n
author,
note.CreatedAt.Unix(),
map[string]string{
- keyGitlabId: id,
+ keyGitlabId: gitlabID,
},
)
return err
@@ -168,7 +167,7 @@ func (gi *gitlabImporter) ensureNote(repo *cache.RepoCache, b *cache.BugCache, n
author,
note.CreatedAt.Unix(),
map[string]string{
- keyGitlabId: id,
+ keyGitlabId: gitlabID,
},
)
return err
@@ -185,10 +184,10 @@ func (gi *gitlabImporter) ensureNote(repo *cache.RepoCache, b *cache.BugCache, n
_, err = b.EditCommentRaw(
author,
note.UpdatedAt.Unix(),
- git.Hash(firstComment.Id()),
+ firstComment.Id(),
issue.Description,
map[string]string{
- keyGitlabId: id,
+ keyGitlabId: gitlabID,
},
)
@@ -211,7 +210,7 @@ func (gi *gitlabImporter) ensureNote(repo *cache.RepoCache, b *cache.BugCache, n
cleanText,
nil,
map[string]string{
- keyGitlabId: id,
+ keyGitlabId: gitlabID,
},
)
@@ -221,7 +220,7 @@ func (gi *gitlabImporter) ensureNote(repo *cache.RepoCache, b *cache.BugCache, n
// if comment was already exported
// search for last comment update
- comment, err := b.Snapshot().SearchComment(hash)
+ comment, err := b.Snapshot().SearchComment(id)
if err != nil {
return err
}
@@ -232,7 +231,7 @@ func (gi *gitlabImporter) ensureNote(repo *cache.RepoCache, b *cache.BugCache, n
_, err = b.EditCommentRaw(
author,
note.UpdatedAt.Unix(),
- git.Hash(comment.Id()),
+ comment.Id(),
cleanText,
nil,
)
@@ -253,7 +252,7 @@ func (gi *gitlabImporter) ensureNote(repo *cache.RepoCache, b *cache.BugCache, n
note.CreatedAt.Unix(),
body,
map[string]string{
- keyGitlabId: id,
+ keyGitlabId: gitlabID,
},
)
@@ -327,7 +326,7 @@ func (gi *gitlabImporter) ensurePerson(repo *cache.RepoCache, id int) (*cache.Id
if err == nil {
return i, nil
}
- if _, ok := err.(identity.ErrMultipleMatch); ok {
+ if _, ok := err.(entity.ErrMultipleMatch); ok {
return nil, err
}
diff --git a/bridge/launchpad/import.go b/bridge/launchpad/import.go
index 63101d9c..7ef11416 100644
--- a/bridge/launchpad/import.go
+++ b/bridge/launchpad/import.go
@@ -4,11 +4,12 @@ import (
"fmt"
"time"
+ "github.com/pkg/errors"
+
"github.com/MichaelMure/git-bug/bridge/core"
"github.com/MichaelMure/git-bug/bug"
"github.com/MichaelMure/git-bug/cache"
- "github.com/MichaelMure/git-bug/identity"
- "github.com/pkg/errors"
+ "github.com/MichaelMure/git-bug/entity"
)
type launchpadImporter struct {
@@ -29,7 +30,7 @@ func (li *launchpadImporter) ensurePerson(repo *cache.RepoCache, owner LPPerson)
if err == nil {
return i, nil
}
- if _, ok := err.(identity.ErrMultipleMatch); ok {
+ if _, ok := err.(entity.ErrMultipleMatch); ok {
return nil, err
}
diff --git a/bug/bug.go b/bug/bug.go
index 7e28954f..eb0337a4 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,18 @@ 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
+func NewErrMultipleMatchBug(matching []entity.Id) *entity.ErrMultipleMatch {
+ return entity.NewErrMultipleMatch("bug", matching)
}
-func (e ErrMultipleMatch) Error() string {
- return fmt.Sprintf("Multiple matching bug found:\n%s", strings.Join(e.Matching, "\n"))
+func NewErrMultipleMatchOp(matching []entity.Id) *entity.ErrMultipleMatch {
+ return entity.NewErrMultipleMatch("operation", matching)
}
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 +52,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 +81,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)
}
}
@@ -95,15 +94,15 @@ func FindLocalBug(repo repository.ClockedRepo, prefix string) (*Bug, error) {
}
if len(matching) > 1 {
- return nil, ErrMultipleMatch{Matching: matching}
+ return nil, NewErrMultipleMatchBug(matching)
}
return ReadLocalBug(repo, matching[0])
}
// 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 +115,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 +277,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 +286,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 +324,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 +455,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 +593,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 +602,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 +611,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..cb0d0f7d 100644
--- a/bug/bug_actions.go
+++ b/bug/bug_actions.go
@@ -65,8 +65,13 @@ func MergeAll(repo repository.ClockedRepo, remote string) <-chan entity.MergeRes
}
for _, remoteRef := range remoteRefs {
- refSplitted := strings.Split(remoteRef, "/")
- id := refSplitted[len(refSplitted)-1]
+ refSplit := strings.Split(remoteRef, "/")
+ id := entity.Id(refSplit[len(refSplit)-1])
+
+ if err := id.Validate(); err != nil {
+ out <- entity.NewMergeInvalidStatus(id, errors.Wrap(err, "invalid ref").Error())
+ continue
+ }
remoteBug, err := readBug(repo, remoteRef)
@@ -81,7 +86,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/bug_test.go b/bug/bug_test.go
index 4c3952eb..4e8a9440 100644
--- a/bug/bug_test.go
+++ b/bug/bug_test.go
@@ -103,7 +103,7 @@ func equivalentBug(t *testing.T, expected, actual *Bug) {
for i := range expected.packs {
for j := range expected.packs[i].Operations {
- actual.packs[i].Operations[j].base().hash = expected.packs[i].Operations[j].base().hash
+ actual.packs[i].Operations[j].base().id = expected.packs[i].Operations[j].base().id
}
}
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 095b7f43..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,32 +16,25 @@ 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) Hash() (git.Hash, error) {
- return hashOperation(op)
+func (op *AddCommentOperation) Id() entity.Id {
+ return idOperation(op)
}
func (op *AddCommentOperation) Apply(snapshot *Snapshot) {
snapshot.addActor(op.Author)
snapshot.addParticipant(op.Author)
- hash, err := op.Hash()
- if err != nil {
- // Should never error unless a programming error happened
- // (covered in OpBase.Validate())
- panic(err)
- }
-
comment := Comment{
- id: string(hash),
+ id: op.Id(),
Message: op.Message,
Author: op.Author,
Files: op.Files,
@@ -50,7 +44,7 @@ func (op *AddCommentOperation) Apply(snapshot *Snapshot) {
snapshot.Comments = append(snapshot.Comments, comment)
item := &AddCommentTimelineItem{
- CommentTimelineItem: NewCommentTimelineItem(hash, comment),
+ CommentTimelineItem: NewCommentTimelineItem(op.Id(), comment),
}
snapshot.Timeline = append(snapshot.Timeline, item)
@@ -72,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 a38d0228..60364cf1 100644
--- a/bug/op_add_comment_test.go
+++ b/bug/op_add_comment_test.go
@@ -5,8 +5,9 @@ import (
"testing"
"time"
- "github.com/MichaelMure/git-bug/identity"
"github.com/stretchr/testify/assert"
+
+ "github.com/MichaelMure/git-bug/identity"
)
func TestAddCommentSerialize(t *testing.T) {
@@ -21,5 +22,9 @@ func TestAddCommentSerialize(t *testing.T) {
err = json.Unmarshal(data, &after)
assert.NoError(t, err)
+ // 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 e52e6254..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,34 +17,27 @@ 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) Hash() (git.Hash, error) {
- return hashOperation(op)
+func (op *CreateOperation) Id() entity.Id {
+ return idOperation(op)
}
func (op *CreateOperation) Apply(snapshot *Snapshot) {
snapshot.addActor(op.Author)
snapshot.addParticipant(op.Author)
- hash, err := op.Hash()
- if err != nil {
- // Should never error unless a programming error happened
- // (covered in OpBase.Validate())
- panic(err)
- }
-
snapshot.Title = op.Title
comment := Comment{
- id: string(hash),
+ id: op.Id(),
Message: op.Message,
Author: op.Author,
UnixTime: timestamp.Timestamp(op.UnixTime),
@@ -55,7 +49,7 @@ func (op *CreateOperation) Apply(snapshot *Snapshot) {
snapshot.Timeline = []TimelineItem{
&CreateTimelineItem{
- CommentTimelineItem: NewCommentTimelineItem(hash, comment),
+ CommentTimelineItem: NewCommentTimelineItem(op.Id(), comment),
},
}
}
@@ -88,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 2a88f256..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)
- hash, err := create.Hash()
- assert.NoError(t, err)
+ id := create.Id()
+ assert.NoError(t, id.Validate())
comment := Comment{
- id: string(hash),
+ id: id,
Author: rene,
Message: "message",
UnixTime: timestamp.Timestamp(create.UnixTime),
@@ -41,7 +41,7 @@ func TestCreate(t *testing.T) {
CreatedAt: create.Time(),
Timeline: []TimelineItem{
&CreateTimelineItem{
- CommentTimelineItem: NewCommentTimelineItem(hash, comment),
+ CommentTimelineItem: NewCommentTimelineItem(id, comment),
},
},
}
@@ -61,5 +61,9 @@ func TestCreateSerialize(t *testing.T) {
err = json.Unmarshal(data, &after)
assert.NoError(t, err)
+ // 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 01832959..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,17 +19,17 @@ var _ Operation = &EditCommentOperation{}
// EditCommentOperation will change a comment in the bug
type EditCommentOperation struct {
OpBase
- Target git.Hash
- 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) Hash() (git.Hash, error) {
- return hashOperation(op)
+func (op *EditCommentOperation) Id() entity.Id {
+ return idOperation(op)
}
func (op *EditCommentOperation) Apply(snapshot *Snapshot) {
@@ -38,9 +41,7 @@ func (op *EditCommentOperation) Apply(snapshot *Snapshot) {
var target TimelineItem
for i, item := range snapshot.Timeline {
- h := item.Hash()
-
- if h == op.Target {
+ if item.Id() == op.Target {
target = snapshot.Timeline[i]
break
}
@@ -52,7 +53,7 @@ func (op *EditCommentOperation) Apply(snapshot *Snapshot) {
}
comment := Comment{
- id: string(op.Target),
+ id: op.Target,
Message: op.Message,
Files: op.Files,
UnixTime: timestamp.Timestamp(op.UnixTime),
@@ -71,7 +72,7 @@ func (op *EditCommentOperation) Apply(snapshot *Snapshot) {
// Updating the corresponding comment
for i := range snapshot.Comments {
- if snapshot.Comments[i].Id() == string(op.Target) {
+ if snapshot.Comments[i].Id() == op.Target {
snapshot.Comments[i].Message = op.Message
snapshot.Comments[i].Files = op.Files
break
@@ -88,8 +89,8 @@ func (op *EditCommentOperation) Validate() error {
return err
}
- if !op.Target.IsValid() {
- 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) {
@@ -99,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
@@ -132,7 +113,7 @@ func (op *EditCommentOperation) UnmarshalJSON(data []byte) error {
}
aux := struct {
- Target git.Hash `json:"target"`
+ Target entity.Id `json:"target"`
Message string `json:"message"`
Files []git.Hash `json:"files"`
}{}
@@ -153,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 git.Hash, 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,
@@ -163,11 +144,11 @@ func NewEditCommentOp(author identity.Interface, unixTime int64, target git.Hash
}
// Convenience function to apply the operation
-func EditComment(b Interface, author identity.Interface, unixTime int64, target git.Hash, 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 git.Hash, 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 ab0f2d21..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, err := create.Hash()
- require.NoError(t, err)
+ id1 := create.Id()
+ require.NoError(t, id1.Validate())
comment1 := NewAddCommentOp(rene, unix, "comment 1", nil)
comment1.Apply(&snapshot)
- hash2, err := comment1.Hash()
- require.NoError(t, err)
+ 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, err := comment2.Hash()
- require.NoError(t, err)
+ 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,5 +85,9 @@ func TestEditCommentSerialize(t *testing.T) {
err = json.Unmarshal(data, &after)
assert.NoError(t, err)
+ // 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 4c019d67..c911de26 100644
--- a/bug/op_label_change.go
+++ b/bug/op_label_change.go
@@ -5,11 +5,11 @@ import (
"fmt"
"sort"
+ "github.com/pkg/errors"
+
+ "github.com/MichaelMure/git-bug/entity"
"github.com/MichaelMure/git-bug/identity"
"github.com/MichaelMure/git-bug/util/timestamp"
-
- "github.com/MichaelMure/git-bug/util/git"
- "github.com/pkg/errors"
)
var _ Operation = &LabelChangeOperation{}
@@ -17,16 +17,16 @@ 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) Hash() (git.Hash, error) {
- return hashOperation(op)
+func (op *LabelChangeOperation) Id() entity.Id {
+ return idOperation(op)
}
// Apply apply the operation
@@ -61,15 +61,8 @@ AddLoop:
return string(snapshot.Labels[i]) < string(snapshot.Labels[j])
})
- hash, err := op.Hash()
- if err != nil {
- // Should never error unless a programming error happened
- // (covered in OpBase.Validate())
- panic(err)
- }
-
item := &LabelChangeTimelineItem{
- hash: hash,
+ id: op.Id(),
Author: op.Author,
UnixTime: timestamp.Timestamp(op.UnixTime),
Added: op.Added,
@@ -103,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
@@ -163,15 +137,15 @@ func NewLabelChangeOperation(author identity.Interface, unixTime int64, added, r
}
type LabelChangeTimelineItem struct {
- hash git.Hash
+ id entity.Id
Author identity.Interface
UnixTime timestamp.Timestamp
Added []Label
Removed []Label
}
-func (l LabelChangeTimelineItem) Hash() git.Hash {
- return l.hash
+func (l LabelChangeTimelineItem) Id() entity.Id {
+ return l.id
}
// Sign post method for gqlgen
diff --git a/bug/op_label_change_test.go b/bug/op_label_change_test.go
index f5550b72..2a93e362 100644
--- a/bug/op_label_change_test.go
+++ b/bug/op_label_change_test.go
@@ -21,5 +21,9 @@ func TestLabelChangeSerialize(t *testing.T) {
err = json.Unmarshal(data, &after)
assert.NoError(t, err)
+ // 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 3cd9f39a..16d32297 100644
--- a/bug/op_noop.go
+++ b/bug/op_noop.go
@@ -3,8 +3,8 @@ package bug
import (
"encoding/json"
+ "github.com/MichaelMure/git-bug/entity"
"github.com/MichaelMure/git-bug/identity"
- "github.com/MichaelMure/git-bug/util/git"
)
var _ Operation = &NoOpOperation{}
@@ -20,8 +20,8 @@ func (op *NoOpOperation) base() *OpBase {
return &op.OpBase
}
-func (op *NoOpOperation) Hash() (git.Hash, error) {
- return hashOperation(op)
+func (op *NoOpOperation) Id() entity.Id {
+ return idOperation(op)
}
func (op *NoOpOperation) Apply(snapshot *Snapshot) {
@@ -32,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 385bc914..ea815948 100644
--- a/bug/op_noop_test.go
+++ b/bug/op_noop_test.go
@@ -21,5 +21,9 @@ func TestNoopSerialize(t *testing.T) {
err = json.Unmarshal(data, &after)
assert.NoError(t, err)
+ // 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 7616a591..f99f836b 100644
--- a/bug/op_set_metadata.go
+++ b/bug/op_set_metadata.go
@@ -2,38 +2,32 @@ package bug
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/git"
)
var _ Operation = &SetMetadataOperation{}
type SetMetadataOperation struct {
OpBase
- Target git.Hash
- 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) Hash() (git.Hash, error) {
- return hashOperation(op)
+func (op *SetMetadataOperation) Id() entity.Id {
+ return idOperation(op)
}
func (op *SetMetadataOperation) Apply(snapshot *Snapshot) {
for _, target := range snapshot.Operations {
- hash, err := target.Hash()
- if err != nil {
- // Should never error unless a programming error happened
- // (covered in OpBase.Validate())
- panic(err)
- }
-
- if hash == op.Target {
+ if target.Id() == op.Target {
base := target.base()
if base.extraMetadata == nil {
@@ -56,35 +50,16 @@ func (op *SetMetadataOperation) Validate() error {
return err
}
- if !op.Target.IsValid() {
- 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
@@ -95,7 +70,7 @@ func (op *SetMetadataOperation) UnmarshalJSON(data []byte) error {
}
aux := struct {
- Target git.Hash `json:"target"`
+ Target entity.Id `json:"target"`
NewMetadata map[string]string `json:"new_metadata"`
}{}
@@ -114,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 git.Hash, 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,
@@ -123,7 +98,7 @@ func NewSetMetadataOp(author identity.Interface, unixTime int64, target git.Hash
}
// Convenience function to apply the operation
-func SetMetadata(b Interface, author identity.Interface, unixTime int64, target git.Hash, 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 a7f9f313..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, err := create.Hash()
- require.NoError(t, err)
+ 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, err := comment.Hash()
- require.NoError(t, err)
+ 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,5 +107,9 @@ func TestSetMetadataSerialize(t *testing.T) {
err = json.Unmarshal(data, &after)
assert.NoError(t, err)
+ // 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 52ba8135..8a245184 100644
--- a/bug/op_set_status.go
+++ b/bug/op_set_status.go
@@ -3,10 +3,11 @@ package bug
import (
"encoding/json"
+ "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/MichaelMure/git-bug/util/timestamp"
- "github.com/pkg/errors"
)
var _ Operation = &SetStatusOperation{}
@@ -14,30 +15,23 @@ 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) Hash() (git.Hash, error) {
- return hashOperation(op)
+func (op *SetStatusOperation) Id() entity.Id {
+ return idOperation(op)
}
func (op *SetStatusOperation) Apply(snapshot *Snapshot) {
snapshot.Status = op.Status
snapshot.addActor(op.Author)
- hash, err := op.Hash()
- if err != nil {
- // Should never error unless a programming error happened
- // (covered in OpBase.Validate())
- panic(err)
- }
-
item := &SetStatusTimelineItem{
- hash: hash,
+ id: op.Id(),
Author: op.Author,
UnixTime: timestamp.Timestamp(op.UnixTime),
Status: op.Status,
@@ -58,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
@@ -114,14 +90,14 @@ func NewSetStatusOp(author identity.Interface, unixTime int64, status Status) *S
}
type SetStatusTimelineItem struct {
- hash git.Hash
+ id entity.Id
Author identity.Interface
UnixTime timestamp.Timestamp
Status Status
}
-func (s SetStatusTimelineItem) Hash() git.Hash {
- return s.hash
+func (s SetStatusTimelineItem) Id() entity.Id {
+ return s.id
}
// Sign post method for gqlgen
diff --git a/bug/op_set_status_test.go b/bug/op_set_status_test.go
index 2506b947..ea032184 100644
--- a/bug/op_set_status_test.go
+++ b/bug/op_set_status_test.go
@@ -21,5 +21,9 @@ func TestSetStatusSerialize(t *testing.T) {
err = json.Unmarshal(data, &after)
assert.NoError(t, err)
+ // 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 31113943..fadd29a9 100644
--- a/bug/op_set_title.go
+++ b/bug/op_set_title.go
@@ -5,10 +5,10 @@ import (
"fmt"
"strings"
+ "github.com/MichaelMure/git-bug/entity"
"github.com/MichaelMure/git-bug/identity"
"github.com/MichaelMure/git-bug/util/timestamp"
- "github.com/MichaelMure/git-bug/util/git"
"github.com/MichaelMure/git-bug/util/text"
)
@@ -17,31 +17,24 @@ 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) Hash() (git.Hash, error) {
- return hashOperation(op)
+func (op *SetTitleOperation) Id() entity.Id {
+ return idOperation(op)
}
func (op *SetTitleOperation) Apply(snapshot *Snapshot) {
snapshot.Title = op.Title
snapshot.addActor(op.Author)
- hash, err := op.Hash()
- if err != nil {
- // Should never error unless a programming error happened
- // (covered in OpBase.Validate())
- panic(err)
- }
-
item := &SetTitleTimelineItem{
- hash: hash,
+ id: op.Id(),
Author: op.Author,
UnixTime: timestamp.Timestamp(op.UnixTime),
Title: op.Title,
@@ -79,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
@@ -139,15 +113,15 @@ func NewSetTitleOp(author identity.Interface, unixTime int64, title string, was
}
type SetTitleTimelineItem struct {
- hash git.Hash
+ id entity.Id
Author identity.Interface
UnixTime timestamp.Timestamp
Title string
Was string
}
-func (s SetTitleTimelineItem) Hash() git.Hash {
- return s.hash
+func (s SetTitleTimelineItem) Id() entity.Id {
+ return s.id
}
// Sign post method for gqlgen
diff --git a/bug/op_set_title_test.go b/bug/op_set_title_test.go
index 1f730596..19cbb12b 100644
--- a/bug/op_set_title_test.go
+++ b/bug/op_set_title_test.go
@@ -21,5 +21,9 @@ func TestSetTitleSerialize(t *testing.T) {
err = json.Unmarshal(data, &after)
assert.NoError(t, err)
+ // enforce creating the IDs
+ before.Id()
+ rene.Id()
+
assert.Equal(t, before, &after)
}
diff --git a/bug/operation.go b/bug/operation.go
index daef7b8c..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
@@ -31,8 +32,8 @@ const (
type Operation interface {
// base return the OpBase of the Operation, for package internal use
base() *OpBase
- // Hash return the hash of the operation, to be used for back references
- Hash() (git.Hash, error)
+ // 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
@@ -53,42 +54,42 @@ type Operation interface {
GetAuthor() identity.Interface
}
-func hashRaw(data []byte) git.Hash {
- hasher := sha256.New()
- // Write can't fail
- _, _ = hasher.Write(data)
- return git.Hash(fmt.Sprintf("%x", hasher.Sum(nil)))
+func deriveId(data []byte) entity.Id {
+ sum := sha256.Sum256(data)
+ return entity.Id(fmt.Sprintf("%x", sum))
}
-// hash compute the hash of the serialized operation
-func hashOperation(op Operation) (git.Hash, error) {
- // TODO: this might not be the best idea: if a single bit change in the output of json.Marshal, this will break.
- // Idea: hash the segment of serialized data (= immutable) instead of the go object in memory
-
+func idOperation(op Operation) entity.Id {
base := op.base()
- if base.hash != "" {
- return base.hash, nil
+ if base.id == "" {
+ // something went really wrong
+ panic("op's id not set")
}
+ 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.
+
+ data, err := json.Marshal(op)
+ if err != nil {
+ panic(err)
+ }
- data, err := json.Marshal(op)
- if err != nil {
- return "", err
+ base.id = deriveId(data)
}
-
- base.hash = hashRaw(data)
-
- return base.hash, nil
+ return base.id
}
// OpBase implement the common code for all operations
type OpBase struct {
- OperationType OperationType
- Author identity.Interface
- UnixTime int64
- Metadata map[string]string
- // Not serialized. Store the op's hash in memory.
- hash git.Hash
+ 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 entity.Id
// Not serialized. Store the extra metadata in memory,
// compiled from SetMetadataOperation.
extraMetadata map[string]string
@@ -100,24 +101,14 @@ func newOpBase(opType OperationType, author identity.Interface, unixTime int64)
OperationType: opType,
Author: author,
UnixTime: unixTime,
+ 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 = deriveId(data)
+
aux := struct {
OperationType OperationType `json:"type"`
Author json.RawMessage `json:"author"`
@@ -192,7 +183,7 @@ func (op *OpBase) SetMetadata(key string, value string) {
}
op.Metadata[key] = value
- op.hash = ""
+ op.id = entity.UnsetId
}
// GetMetadata retrieve arbitrary metadata about the operation
diff --git a/bug/operation_pack.go b/bug/operation_pack.go
index 5f3e9da8..86e4178e 100644
--- a/bug/operation_pack.go
+++ b/bug/operation_pack.go
@@ -63,9 +63,6 @@ func (opp *OperationPack) UnmarshalJSON(data []byte) error {
return err
}
- // Compute the hash of the operation
- op.base().hash = hashRaw(raw)
-
opp.Operations = append(opp.Operations, op)
}
diff --git a/bug/operation_pack_test.go b/bug/operation_pack_test.go
index 09d159af..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)
- ensureHash(t, opp)
+ ensureIDs(t, opp)
assert.Equal(t, opp, opp2)
}
-func ensureHash(t *testing.T, opp *OperationPack) {
+func ensureIDs(t *testing.T, opp *OperationPack) {
for _, op := range opp.Operations {
- _, err := op.Hash()
- require.NoError(t, err)
+ 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 0ddb61c2..69c66dc8 100644
--- a/bug/operation_test.go
+++ b/bug/operation_test.go
@@ -79,7 +79,7 @@ func TestMetadata(t *testing.T) {
require.Equal(t, val, "value")
}
-func TestHash(t *testing.T) {
+func TestID(t *testing.T) {
repo := repository.CreateTestRepo(false)
defer repository.CleanupTestRepos(t, repo)
@@ -94,27 +94,27 @@ func TestHash(t *testing.T) {
b, op, err := Create(rene, time.Now().Unix(), "title", "message")
require.Nil(t, err)
- h1, err := op.Hash()
- require.Nil(t, err)
+ id1 := op.Id()
+ require.NoError(t, id1.Validate())
err = b.Commit(repo)
require.Nil(t, err)
op2 := b.FirstOp()
- h2, err := op2.Hash()
- require.Nil(t, err)
+ id2 := op2.Id()
+ require.NoError(t, id2.Validate())
- require.Equal(t, h1, h2)
+ require.Equal(t, id1, id2)
- b2, err := ReadLocalBug(repo, b.id)
+ b2, err := ReadLocalBug(repo, b.Id())
require.Nil(t, err)
op3 := b2.FirstOp()
- h3, err := op3.Hash()
- require.Nil(t, err)
+ id3 := op3.Id()
+ require.NoError(t, id3.Validate())
- require.Equal(t, h1, h3)
+ require.Equal(t, id1, id3)
}
}
diff --git a/bug/snapshot.go b/bug/snapshot.go
index f1da8099..39366c6d 100644
--- a/bug/snapshot.go
+++ b/bug/snapshot.go
@@ -4,13 +4,13 @@ import (
"fmt"
"time"
+ "github.com/MichaelMure/git-bug/entity"
"github.com/MichaelMure/git-bug/identity"
- "github.com/MichaelMure/git-bug/util/git"
)
// 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
@@ -27,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 {
@@ -60,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(hash git.Hash) (TimelineItem, error) {
+func (snap *Snapshot) SearchTimelineItem(id entity.Id) (TimelineItem, error) {
for i := range snap.Timeline {
- if snap.Timeline[i].Hash() == hash {
+ if snap.Timeline[i].Id() == id {
return snap.Timeline[i], nil
}
}
@@ -71,9 +66,9 @@ func (snap *Snapshot) SearchTimelineItem(hash git.Hash) (TimelineItem, error) {
}
// SearchComment will search for a comment matching the given hash
-func (snap *Snapshot) SearchComment(hash git.Hash) (*Comment, error) {
+func (snap *Snapshot) SearchComment(id entity.Id) (*Comment, error) {
for _, c := range snap.Comments {
- if c.id == hash.String() {
+ if c.id == id {
return &c, nil
}
}
@@ -104,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
@@ -114,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
@@ -124,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
@@ -134,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 d8ee2c6b..4af1b92a 100644
--- a/bug/timeline.go
+++ b/bug/timeline.go
@@ -3,14 +3,15 @@ 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"
)
type TimelineItem interface {
- // Hash return the hash of the item
- Hash() git.Hash
+ // ID return the identifier of the item
+ 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 {
- hash git.Hash
+ id entity.Id
Author identity.Interface
Message string
Files []git.Hash
@@ -34,9 +35,9 @@ type CommentTimelineItem struct {
History []CommentHistoryStep
}
-func NewCommentTimelineItem(hash git.Hash, comment Comment) CommentTimelineItem {
+func NewCommentTimelineItem(ID entity.Id, comment Comment) CommentTimelineItem {
return CommentTimelineItem{
- hash: hash,
+ id: ID,
Author: comment.Author,
Message: comment.Message,
Files: comment.Files,
@@ -51,8 +52,8 @@ func NewCommentTimelineItem(hash git.Hash, comment Comment) CommentTimelineItem
}
}
-func (c *CommentTimelineItem) Hash() git.Hash {
- return c.hash
+func (c *CommentTimelineItem) Id() entity.Id {
+ return c.id
}
// Append will append a new comment in the history and update the other values
diff --git a/cache/bug_cache.go b/cache/bug_cache.go
index 758fb0b7..6a220f49 100644
--- a/cache/bug_cache.go
+++ b/cache/bug_cache.go
@@ -2,13 +2,15 @@ package cache
import (
"fmt"
- "strings"
"time"
"github.com/MichaelMure/git-bug/bug"
+ "github.com/MichaelMure/git-bug/entity"
"github.com/MichaelMure/git-bug/util/git"
)
+var ErrNoMatchingOp = fmt.Errorf("no matching operation found")
+
// BugCache is a wrapper around a Bug. It provide multiple functions:
//
// 1. Provide a higher level API to use than the raw API from Bug.
@@ -29,49 +31,25 @@ func (c *BugCache) Snapshot() *bug.Snapshot {
return c.bug.Snapshot()
}
-func (c *BugCache) Id() string {
+func (c *BugCache) Id() entity.Id {
return c.bug.Id()
}
-func (c *BugCache) HumanId() string {
- return c.bug.HumanId()
-}
-
func (c *BugCache) notifyUpdated() error {
return c.repoCache.bugUpdated(c.bug.Id())
}
-var ErrNoMatchingOp = fmt.Errorf("no matching operation found")
-
-type ErrMultipleMatchOp struct {
- Matching []git.Hash
-}
-
-func (e ErrMultipleMatchOp) Error() string {
- casted := make([]string, len(e.Matching))
-
- for i := range e.Matching {
- casted[i] = string(e.Matching[i])
- }
-
- return fmt.Sprintf("Multiple matching operation found:\n%s", strings.Join(casted, "\n"))
-}
-
// ResolveOperationWithMetadata will find an operation that has the matching metadata
-func (c *BugCache) ResolveOperationWithMetadata(key string, value string) (git.Hash, error) {
+func (c *BugCache) ResolveOperationWithMetadata(key string, value string) (entity.Id, error) {
// preallocate but empty
- matching := make([]git.Hash, 0, 5)
+ matching := make([]entity.Id, 0, 5)
it := bug.NewOperationIterator(c.bug)
for it.Next() {
op := it.Value()
opValue, ok := op.GetMetadata(key)
if ok && value == opValue {
- h, err := op.Hash()
- if err != nil {
- return "", err
- }
- matching = append(matching, h)
+ matching = append(matching, op.Id())
}
}
@@ -80,7 +58,7 @@ func (c *BugCache) ResolveOperationWithMetadata(key string, value string) (git.H
}
if len(matching) > 1 {
- return "", ErrMultipleMatchOp{Matching: matching}
+ return "", bug.NewErrMultipleMatchOp(matching)
}
return matching[0], nil
@@ -232,7 +210,7 @@ func (c *BugCache) SetTitleRaw(author *IdentityCache, unixTime int64, title stri
return op, c.notifyUpdated()
}
-func (c *BugCache) EditComment(target git.Hash, message string) (*bug.EditCommentOperation, error) {
+func (c *BugCache) EditComment(target entity.Id, message string) (*bug.EditCommentOperation, error) {
author, err := c.repoCache.GetUserIdentity()
if err != nil {
return nil, err
@@ -241,7 +219,7 @@ func (c *BugCache) EditComment(target git.Hash, message string) (*bug.EditCommen
return c.EditCommentRaw(author, time.Now().Unix(), target, message, nil)
}
-func (c *BugCache) EditCommentRaw(author *IdentityCache, unixTime int64, target git.Hash, message string, metadata map[string]string) (*bug.EditCommentOperation, error) {
+func (c *BugCache) EditCommentRaw(author *IdentityCache, unixTime int64, target entity.Id, message string, metadata map[string]string) (*bug.EditCommentOperation, error) {
op, err := bug.EditComment(c.bug, author.Identity, unixTime, target, message)
if err != nil {
return nil, err
@@ -254,7 +232,7 @@ func (c *BugCache) EditCommentRaw(author *IdentityCache, unixTime int64, target
return op, c.notifyUpdated()
}
-func (c *BugCache) SetMetadata(target git.Hash, newMetadata map[string]string) (*bug.SetMetadataOperation, error) {
+func (c *BugCache) SetMetadata(target entity.Id, newMetadata map[string]string) (*bug.SetMetadataOperation, error) {
author, err := c.repoCache.GetUserIdentity()
if err != nil {
return nil, err
@@ -263,7 +241,7 @@ func (c *BugCache) SetMetadata(target git.Hash, newMetadata map[string]string) (
return c.SetMetadataRaw(author, time.Now().Unix(), target, newMetadata)
}
-func (c *BugCache) SetMetadataRaw(author *IdentityCache, unixTime int64, target git.Hash, newMetadata map[string]string) (*bug.SetMetadataOperation, error) {
+func (c *BugCache) SetMetadataRaw(author *IdentityCache, unixTime int64, target entity.Id, newMetadata map[string]string) (*bug.SetMetadataOperation, error) {
op, err := bug.SetMetadata(c.bug, author.Identity, unixTime, target, newMetadata)
if err != nil {
return nil, err
diff --git a/cache/bug_excerpt.go b/cache/bug_excerpt.go
index 8e9e5e37..e053f9e4 100644
--- a/cache/bug_excerpt.go
+++ b/cache/bug_excerpt.go
@@ -5,6 +5,7 @@ import (
"fmt"
"github.com/MichaelMure/git-bug/bug"
+ "github.com/MichaelMure/git-bug/entity"
"github.com/MichaelMure/git-bug/identity"
"github.com/MichaelMure/git-bug/util/lamport"
)
@@ -17,7 +18,7 @@ func init() {
// BugExcerpt hold a subset of the bug values to be able to sort and filter bugs
// efficiently without having to read and compile each raw bugs.
type BugExcerpt struct {
- Id string
+ Id entity.Id
CreateLamportTime lamport.Time
EditLamportTime lamport.Time
@@ -28,14 +29,14 @@ type BugExcerpt struct {
Labels []bug.Label
Title string
LenComments int
- Actors []string
- Participants []string
+ Actors []entity.Id
+ Participants []entity.Id
// If author is identity.Bare, LegacyAuthor is set
// If author is identity.Identity, AuthorId is set and data is deported
// in a IdentityExcerpt
LegacyAuthor LegacyAuthorExcerpt
- AuthorId string
+ AuthorId entity.Id
CreateMetadata map[string]string
}
@@ -60,12 +61,12 @@ func (l LegacyAuthorExcerpt) DisplayName() string {
}
func NewBugExcerpt(b bug.Interface, snap *bug.Snapshot) *BugExcerpt {
- participantsIds := make([]string, len(snap.Participants))
+ participantsIds := make([]entity.Id, len(snap.Participants))
for i, participant := range snap.Participants {
participantsIds[i] = participant.Id()
}
- actorsIds := make([]string, len(snap.Actors))
+ actorsIds := make([]entity.Id, len(snap.Actors))
for i, actor := range snap.Actors {
actorsIds[i] = actor.Id()
}
@@ -100,10 +101,6 @@ func NewBugExcerpt(b bug.Interface, snap *bug.Snapshot) *BugExcerpt {
return e
}
-func (b *BugExcerpt) HumanId() string {
- return bug.FormatHumanID(b.Id)
-}
-
/*
* Sorting
*/
diff --git a/cache/identity_excerpt.go b/cache/identity_excerpt.go
index 3ac13903..18514e9a 100644
--- a/cache/identity_excerpt.go
+++ b/cache/identity_excerpt.go
@@ -5,6 +5,7 @@ import (
"fmt"
"strings"
+ "github.com/MichaelMure/git-bug/entity"
"github.com/MichaelMure/git-bug/identity"
)
@@ -17,7 +18,7 @@ func init() {
// filter identities efficiently without having to read and compile each raw
// identity.
type IdentityExcerpt struct {
- Id string
+ Id entity.Id
Name string
Login string
@@ -33,10 +34,6 @@ func NewIdentityExcerpt(i *identity.Identity) *IdentityExcerpt {
}
}
-func (i *IdentityExcerpt) HumanId() string {
- return identity.FormatHumanID(i.Id)
-}
-
// DisplayName return a non-empty string to display, representing the
// identity, based on the non-empty values.
func (i *IdentityExcerpt) DisplayName() string {
@@ -54,7 +51,7 @@ func (i *IdentityExcerpt) DisplayName() string {
// Match matches a query with the identity name, login and ID prefixes
func (i *IdentityExcerpt) Match(query string) bool {
- return strings.HasPrefix(i.Id, query) ||
+ return i.Id.HasPrefix(query) ||
strings.Contains(strings.ToLower(i.Name), query) ||
strings.Contains(strings.ToLower(i.Login), query)
}
diff --git a/cache/repo_cache.go b/cache/repo_cache.go
index a80bc7c9..d6e8857d 100644
--- a/cache/repo_cache.go
+++ b/cache/repo_cache.go
@@ -10,16 +10,16 @@ import (
"path"
"sort"
"strconv"
- "strings"
"time"
+ "github.com/pkg/errors"
+
"github.com/MichaelMure/git-bug/bug"
"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"
"github.com/MichaelMure/git-bug/util/process"
- "github.com/pkg/errors"
)
const bugCacheFile = "bug-cache"
@@ -58,24 +58,24 @@ type RepoCache struct {
repo repository.ClockedRepo
// excerpt of bugs data for all bugs
- bugExcerpts map[string]*BugExcerpt
+ bugExcerpts map[entity.Id]*BugExcerpt
// bug loaded in memory
- bugs map[string]*BugCache
+ bugs map[entity.Id]*BugCache
// excerpt of identities data for all identities
- identitiesExcerpts map[string]*IdentityExcerpt
+ identitiesExcerpts map[entity.Id]*IdentityExcerpt
// identities loaded in memory
- identities map[string]*IdentityCache
+ identities map[entity.Id]*IdentityCache
// the user identity's id, if known
- userIdentityId string
+ userIdentityId entity.Id
}
func NewRepoCache(r repository.ClockedRepo) (*RepoCache, error) {
c := &RepoCache{
repo: r,
- bugs: make(map[string]*BugCache),
- identities: make(map[string]*IdentityCache),
+ bugs: make(map[entity.Id]*BugCache),
+ identities: make(map[entity.Id]*IdentityCache),
}
err := c.lock()
@@ -191,7 +191,7 @@ func (c *RepoCache) Close() error {
// bugUpdated is a callback to trigger when the excerpt of a bug changed,
// that is each time a bug is updated
-func (c *RepoCache) bugUpdated(id string) error {
+func (c *RepoCache) bugUpdated(id entity.Id) error {
b, ok := c.bugs[id]
if !ok {
panic("missing bug in the cache")
@@ -205,7 +205,7 @@ func (c *RepoCache) bugUpdated(id string) error {
// identityUpdated is a callback to trigger when the excerpt of an identity
// changed, that is each time an identity is updated
-func (c *RepoCache) identityUpdated(id string) error {
+func (c *RepoCache) identityUpdated(id entity.Id) error {
i, ok := c.identities[id]
if !ok {
panic("missing identity in the cache")
@@ -237,7 +237,7 @@ func (c *RepoCache) loadBugCache() error {
aux := struct {
Version uint
- Excerpts map[string]*BugExcerpt
+ Excerpts map[entity.Id]*BugExcerpt
}{}
err = decoder.Decode(&aux)
@@ -266,7 +266,7 @@ func (c *RepoCache) loadIdentityCache() error {
aux := struct {
Version uint
- Excerpts map[string]*IdentityExcerpt
+ Excerpts map[entity.Id]*IdentityExcerpt
}{}
err = decoder.Decode(&aux)
@@ -299,7 +299,7 @@ func (c *RepoCache) writeBugCache() error {
aux := struct {
Version uint
- Excerpts map[string]*BugExcerpt
+ Excerpts map[entity.Id]*BugExcerpt
}{
Version: formatVersion,
Excerpts: c.bugExcerpts,
@@ -331,7 +331,7 @@ func (c *RepoCache) writeIdentityCache() error {
aux := struct {
Version uint
- Excerpts map[string]*IdentityExcerpt
+ Excerpts map[entity.Id]*IdentityExcerpt
}{
Version: formatVersion,
Excerpts: c.identitiesExcerpts,
@@ -368,7 +368,7 @@ func identityCacheFilePath(repo repository.Repo) string {
func (c *RepoCache) buildCache() error {
_, _ = fmt.Fprintf(os.Stderr, "Building identity cache... ")
- c.identitiesExcerpts = make(map[string]*IdentityExcerpt)
+ c.identitiesExcerpts = make(map[entity.Id]*IdentityExcerpt)
allIdentities := identity.ReadAllLocalIdentities(c.repo)
@@ -384,7 +384,7 @@ func (c *RepoCache) buildCache() error {
_, _ = fmt.Fprintf(os.Stderr, "Building bug cache... ")
- c.bugExcerpts = make(map[string]*BugExcerpt)
+ c.bugExcerpts = make(map[entity.Id]*BugExcerpt)
allBugs := bug.ReadAllLocalBugs(c.repo)
@@ -402,7 +402,7 @@ func (c *RepoCache) buildCache() error {
}
// ResolveBug retrieve a bug matching the exact given id
-func (c *RepoCache) ResolveBug(id string) (*BugCache, error) {
+func (c *RepoCache) ResolveBug(id entity.Id) (*BugCache, error) {
cached, ok := c.bugs[id]
if ok {
return cached, nil
@@ -420,7 +420,7 @@ func (c *RepoCache) ResolveBug(id string) (*BugCache, error) {
}
// ResolveBugExcerpt retrieve a BugExcerpt matching the exact given id
-func (c *RepoCache) ResolveBugExcerpt(id string) (*BugExcerpt, error) {
+func (c *RepoCache) ResolveBugExcerpt(id entity.Id) (*BugExcerpt, error) {
e, ok := c.bugExcerpts[id]
if !ok {
return nil, bug.ErrBugNotExist
@@ -433,16 +433,16 @@ func (c *RepoCache) ResolveBugExcerpt(id string) (*BugExcerpt, error) {
// bugs match.
func (c *RepoCache) ResolveBugPrefix(prefix string) (*BugCache, error) {
// preallocate but empty
- matching := make([]string, 0, 5)
+ matching := make([]entity.Id, 0, 5)
for id := range c.bugExcerpts {
- if strings.HasPrefix(id, prefix) {
+ if id.HasPrefix(prefix) {
matching = append(matching, id)
}
}
if len(matching) > 1 {
- return nil, bug.ErrMultipleMatch{Matching: matching}
+ return nil, bug.NewErrMultipleMatchBug(matching)
}
if len(matching) == 0 {
@@ -457,7 +457,7 @@ func (c *RepoCache) ResolveBugPrefix(prefix string) (*BugCache, error) {
// match.
func (c *RepoCache) ResolveBugCreateMetadata(key string, value string) (*BugCache, error) {
// preallocate but empty
- matching := make([]string, 0, 5)
+ matching := make([]entity.Id, 0, 5)
for id, excerpt := range c.bugExcerpts {
if excerpt.CreateMetadata[key] == value {
@@ -466,7 +466,7 @@ func (c *RepoCache) ResolveBugCreateMetadata(key string, value string) (*BugCach
}
if len(matching) > 1 {
- return nil, bug.ErrMultipleMatch{Matching: matching}
+ return nil, bug.NewErrMultipleMatchBug(matching)
}
if len(matching) == 0 {
@@ -477,7 +477,7 @@ func (c *RepoCache) ResolveBugCreateMetadata(key string, value string) (*BugCach
}
// QueryBugs return the id of all Bug matching the given Query
-func (c *RepoCache) QueryBugs(query *Query) []string {
+func (c *RepoCache) QueryBugs(query *Query) []entity.Id {
if query == nil {
return c.AllBugsIds()
}
@@ -509,7 +509,7 @@ func (c *RepoCache) QueryBugs(query *Query) []string {
sort.Sort(sorter)
- result := make([]string, len(filtered))
+ result := make([]entity.Id, len(filtered))
for i, val := range filtered {
result[i] = val.Id
@@ -519,8 +519,8 @@ func (c *RepoCache) QueryBugs(query *Query) []string {
}
// AllBugsIds return all known bug ids
-func (c *RepoCache) AllBugsIds() []string {
- result := make([]string, len(c.bugExcerpts))
+func (c *RepoCache) AllBugsIds() []entity.Id {
+ result := make([]entity.Id, len(c.bugExcerpts))
i := 0
for _, excerpt := range c.bugExcerpts {
@@ -778,7 +778,7 @@ func repoIsAvailable(repo repository.Repo) error {
}
// ResolveIdentity retrieve an identity matching the exact given id
-func (c *RepoCache) ResolveIdentity(id string) (*IdentityCache, error) {
+func (c *RepoCache) ResolveIdentity(id entity.Id) (*IdentityCache, error) {
cached, ok := c.identities[id]
if ok {
return cached, nil
@@ -796,7 +796,7 @@ func (c *RepoCache) ResolveIdentity(id string) (*IdentityCache, error) {
}
// ResolveIdentityExcerpt retrieve a IdentityExcerpt matching the exact given id
-func (c *RepoCache) ResolveIdentityExcerpt(id string) (*IdentityExcerpt, error) {
+func (c *RepoCache) ResolveIdentityExcerpt(id entity.Id) (*IdentityExcerpt, error) {
e, ok := c.identitiesExcerpts[id]
if !ok {
return nil, identity.ErrIdentityNotExist
@@ -809,16 +809,16 @@ func (c *RepoCache) ResolveIdentityExcerpt(id string) (*IdentityExcerpt, error)
// It fails if multiple identities match.
func (c *RepoCache) ResolveIdentityPrefix(prefix string) (*IdentityCache, error) {
// preallocate but empty
- matching := make([]string, 0, 5)
+ matching := make([]entity.Id, 0, 5)
for id := range c.identitiesExcerpts {
- if strings.HasPrefix(id, prefix) {
+ if id.HasPrefix(prefix) {
matching = append(matching, id)
}
}
if len(matching) > 1 {
- return nil, identity.ErrMultipleMatch{Matching: matching}
+ return nil, identity.NewErrMultipleMatch(matching)
}
if len(matching) == 0 {
@@ -832,7 +832,7 @@ func (c *RepoCache) ResolveIdentityPrefix(prefix string) (*IdentityCache, error)
// one of it's version. If multiple version have the same key, the first defined take precedence.
func (c *RepoCache) ResolveIdentityImmutableMetadata(key string, value string) (*IdentityCache, error) {
// preallocate but empty
- matching := make([]string, 0, 5)
+ matching := make([]entity.Id, 0, 5)
for id, i := range c.identitiesExcerpts {
if i.ImmutableMetadata[key] == value {
@@ -841,7 +841,7 @@ func (c *RepoCache) ResolveIdentityImmutableMetadata(key string, value string) (
}
if len(matching) > 1 {
- return nil, identity.ErrMultipleMatch{Matching: matching}
+ return nil, identity.NewErrMultipleMatch(matching)
}
if len(matching) == 0 {
@@ -852,8 +852,8 @@ func (c *RepoCache) ResolveIdentityImmutableMetadata(key string, value string) (
}
// AllIdentityIds return all known identity ids
-func (c *RepoCache) AllIdentityIds() []string {
- result := make([]string, len(c.identitiesExcerpts))
+func (c *RepoCache) AllIdentityIds() []entity.Id {
+ result := make([]entity.Id, len(c.identitiesExcerpts))
i := 0
for _, excerpt := range c.identitiesExcerpts {
diff --git a/cache/repo_cache_test.go b/cache/repo_cache_test.go
index 3e81674a..c3bd3cc4 100644
--- a/cache/repo_cache_test.go
+++ b/cache/repo_cache_test.go
@@ -57,14 +57,14 @@ func TestCache(t *testing.T) {
require.NoError(t, err)
_, err = cache.ResolveIdentityExcerpt(iden1.Id())
require.NoError(t, err)
- _, err = cache.ResolveIdentityPrefix(iden1.Id()[:10])
+ _, err = cache.ResolveIdentityPrefix(iden1.Id().String()[:10])
require.NoError(t, err)
_, err = cache.ResolveBug(bug1.Id())
require.NoError(t, err)
_, err = cache.ResolveBugExcerpt(bug1.Id())
require.NoError(t, err)
- _, err = cache.ResolveBugPrefix(bug1.Id()[:10])
+ _, err = cache.ResolveBugPrefix(bug1.Id().String()[:10])
require.NoError(t, err)
// Querying
@@ -91,14 +91,14 @@ func TestCache(t *testing.T) {
require.NoError(t, err)
_, err = cache.ResolveIdentityExcerpt(iden1.Id())
require.NoError(t, err)
- _, err = cache.ResolveIdentityPrefix(iden1.Id()[:10])
+ _, err = cache.ResolveIdentityPrefix(iden1.Id().String()[:10])
require.NoError(t, err)
_, err = cache.ResolveBug(bug1.Id())
require.NoError(t, err)
_, err = cache.ResolveBugExcerpt(bug1.Id())
require.NoError(t, err)
- _, err = cache.ResolveBugPrefix(bug1.Id()[:10])
+ _, err = cache.ResolveBugPrefix(bug1.Id().String()[:10])
require.NoError(t, err)
}
diff --git a/commands/add.go b/commands/add.go
index d199b4c7..ff4f9529 100644
--- a/commands/add.go
+++ b/commands/add.go
@@ -49,7 +49,7 @@ func runAddBug(cmd *cobra.Command, args []string) error {
return err
}
- fmt.Printf("%s created\n", b.HumanId())
+ fmt.Printf("%s created\n", b.Id().Human())
return nil
}
diff --git a/commands/comment.go b/commands/comment.go
index cb9f3691..33bae65d 100644
--- a/commands/comment.go
+++ b/commands/comment.go
@@ -39,7 +39,7 @@ func commentsTextOutput(comments []bug.Comment) {
}
fmt.Printf("Author: %s\n", colors.Magenta(comment.Author.DisplayName()))
- fmt.Printf("Id: %s\n", colors.Cyan(comment.HumanId()))
+ fmt.Printf("Id: %s\n", colors.Cyan(comment.Id().Human()))
fmt.Printf("Date: %s\n\n", comment.FormatTime())
fmt.Println(text.LeftPad(comment.Message, 4))
}
diff --git a/commands/ls-id.go b/commands/ls-id.go
index 47f29792..22357eb4 100644
--- a/commands/ls-id.go
+++ b/commands/ls-id.go
@@ -2,11 +2,11 @@ package commands
import (
"fmt"
- "strings"
+
+ "github.com/spf13/cobra"
"github.com/MichaelMure/git-bug/cache"
"github.com/MichaelMure/git-bug/util/interrupt"
- "github.com/spf13/cobra"
)
func runLsID(cmd *cobra.Command, args []string) error {
@@ -24,7 +24,7 @@ func runLsID(cmd *cobra.Command, args []string) error {
}
for _, id := range backend.AllBugsIds() {
- if prefix == "" || strings.HasPrefix(id, prefix) {
+ if prefix == "" || id.HasPrefix(prefix) {
fmt.Println(id)
}
}
diff --git a/commands/ls.go b/commands/ls.go
index 94f0d0d6..9c32642e 100644
--- a/commands/ls.go
+++ b/commands/ls.go
@@ -70,7 +70,7 @@ func runLsBug(cmd *cobra.Command, args []string) error {
authorFmt := text.LeftPadMaxLine(name, 15, 0)
fmt.Printf("%s %s\t%s\t%s\tC:%d L:%d\n",
- colors.Cyan(b.HumanId()),
+ colors.Cyan(b.Id.Human()),
colors.Yellow(b.Status),
titleFmt,
colors.Magenta(authorFmt),
diff --git a/commands/pull.go b/commands/pull.go
index 5df10dca..0439ab41 100644
--- a/commands/pull.go
+++ b/commands/pull.go
@@ -4,11 +4,11 @@ import (
"errors"
"fmt"
- "github.com/MichaelMure/git-bug/bug"
+ "github.com/spf13/cobra"
+
"github.com/MichaelMure/git-bug/cache"
"github.com/MichaelMure/git-bug/entity"
"github.com/MichaelMure/git-bug/util/interrupt"
- "github.com/spf13/cobra"
)
func runPull(cmd *cobra.Command, args []string) error {
@@ -45,7 +45,7 @@ func runPull(cmd *cobra.Command, args []string) error {
}
if result.Status != entity.MergeStatusNothing {
- fmt.Printf("%s: %s\n", bug.FormatHumanID(result.Id), result)
+ fmt.Printf("%s: %s\n", result.Id.Human(), result)
}
}
diff --git a/commands/select.go b/commands/select.go
index efe48b5e..7c40df5c 100644
--- a/commands/select.go
+++ b/commands/select.go
@@ -34,7 +34,7 @@ func runSelect(cmd *cobra.Command, args []string) error {
return err
}
- fmt.Printf("selected bug %s: %s\n", b.HumanId(), b.Snapshot().Title)
+ fmt.Printf("selected bug %s: %s\n", b.Id().Human(), b.Snapshot().Title)
return nil
}
diff --git a/commands/select/select.go b/commands/select/select.go
index b080d277..fdc87154 100644
--- a/commands/select/select.go
+++ b/commands/select/select.go
@@ -7,11 +7,12 @@ import (
"os"
"path"
+ "github.com/pkg/errors"
+
"github.com/MichaelMure/git-bug/bug"
"github.com/MichaelMure/git-bug/cache"
+ "github.com/MichaelMure/git-bug/entity"
"github.com/MichaelMure/git-bug/repository"
- "github.com/MichaelMure/git-bug/util/git"
- "github.com/pkg/errors"
)
const selectFile = "select"
@@ -69,7 +70,7 @@ func ResolveBug(repo *cache.RepoCache, args []string) (*cache.BugCache, []string
}
// Select will select a bug for future use
-func Select(repo *cache.RepoCache, id string) error {
+func Select(repo *cache.RepoCache, id entity.Id) error {
selectPath := selectFilePath(repo)
f, err := os.OpenFile(selectPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
@@ -77,7 +78,7 @@ func Select(repo *cache.RepoCache, id string) error {
return err
}
- _, err = f.WriteString(id)
+ _, err = f.WriteString(id.String())
if err != nil {
return err
}
@@ -112,8 +113,8 @@ func selected(repo *cache.RepoCache) (*cache.BugCache, error) {
return nil, fmt.Errorf("the select file should be < 100 bytes")
}
- h := git.Hash(buf)
- if !h.IsValid() {
+ id := entity.Id(buf)
+ if err := id.Validate(); err != nil {
err = os.Remove(selectPath)
if err != nil {
return nil, errors.Wrap(err, "error while removing invalid select file")
@@ -122,7 +123,7 @@ func selected(repo *cache.RepoCache) (*cache.BugCache, error) {
return nil, fmt.Errorf("select file in invalid, removing it")
}
- b, err := repo.ResolveBug(string(h))
+ b, err := repo.ResolveBug(id)
if err != nil {
return nil, err
}
diff --git a/commands/select/select_test.go b/commands/select/select_test.go
index 393ec88b..989d6b3c 100644
--- a/commands/select/select_test.go
+++ b/commands/select/select_test.go
@@ -52,12 +52,12 @@ func TestSelect(t *testing.T) {
require.Equal(t, b1.Id(), b3.Id())
// override selection with same id
- b4, _, err := ResolveBug(repoCache, []string{b1.Id()})
+ b4, _, err := ResolveBug(repoCache, []string{b1.Id().String()})
require.NoError(t, err)
require.Equal(t, b1.Id(), b4.Id())
// override selection with a prefix
- b5, _, err := ResolveBug(repoCache, []string{b1.HumanId()})
+ b5, _, err := ResolveBug(repoCache, []string{b1.Id().Human()})
require.NoError(t, err)
require.Equal(t, b1.Id(), b5.Id())
@@ -67,7 +67,7 @@ func TestSelect(t *testing.T) {
require.Equal(t, b1.Id(), b6.Id())
// override with a different id
- b7, _, err := ResolveBug(repoCache, []string{b2.Id()})
+ b7, _, err := ResolveBug(repoCache, []string{b2.Id().String()})
require.NoError(t, err)
require.Equal(t, b2.Id(), b7.Id())
diff --git a/commands/show.go b/commands/show.go
index 41dc5851..0bb3dc4a 100644
--- a/commands/show.go
+++ b/commands/show.go
@@ -46,7 +46,7 @@ func runShowBug(cmd *cobra.Command, args []string) error {
case "createTime":
fmt.Printf("%s\n", firstComment.FormatTime())
case "humanId":
- fmt.Printf("%s\n", snapshot.HumanId())
+ fmt.Printf("%s\n", snapshot.Id().Human())
case "id":
fmt.Printf("%s\n", snapshot.Id())
case "labels":
@@ -62,7 +62,7 @@ func runShowBug(cmd *cobra.Command, args []string) error {
fmt.Printf("%s\n", p.DisplayName())
}
case "shortId":
- fmt.Printf("%s\n", snapshot.HumanId())
+ fmt.Printf("%s\n", snapshot.Id().Human())
case "status":
fmt.Printf("%s\n", snapshot.Status)
case "title":
@@ -77,7 +77,7 @@ func runShowBug(cmd *cobra.Command, args []string) error {
// Header
fmt.Printf("[%s] %s %s\n\n",
colors.Yellow(snapshot.Status),
- colors.Cyan(snapshot.HumanId()),
+ colors.Cyan(snapshot.Id().Human()),
snapshot.Title,
)
diff --git a/commands/user.go b/commands/user.go
index 8cb64491..254abf2f 100644
--- a/commands/user.go
+++ b/commands/user.go
@@ -41,7 +41,7 @@ func runUser(cmd *cobra.Command, args []string) error {
case "email":
fmt.Printf("%s\n", id.Email())
case "humanId":
- fmt.Printf("%s\n", id.HumanId())
+ fmt.Printf("%s\n", id.Id().Human())
case "id":
fmt.Printf("%s\n", id.Id())
case "lastModification":
diff --git a/commands/user_ls.go b/commands/user_ls.go
index 23ecea73..609ff5a4 100644
--- a/commands/user_ls.go
+++ b/commands/user_ls.go
@@ -24,7 +24,7 @@ func runUserLs(cmd *cobra.Command, args []string) error {
}
fmt.Printf("%s %s\n",
- colors.Cyan(i.HumanId()),
+ colors.Cyan(i.Id.Human()),
i.DisplayName(),
)
}
diff --git a/entity/err.go b/entity/err.go
new file mode 100644
index 00000000..7022305c
--- /dev/null
+++ b/entity/err.go
@@ -0,0 +1,27 @@
+package entity
+
+import (
+ "fmt"
+ "strings"
+)
+
+type ErrMultipleMatch struct {
+ entityType string
+ Matching []Id
+}
+
+func NewErrMultipleMatch(entityType string, matching []Id) *ErrMultipleMatch {
+ return &ErrMultipleMatch{entityType: entityType, Matching: matching}
+}
+
+func (e ErrMultipleMatch) Error() string {
+ matching := make([]string, len(e.Matching))
+
+ for i, match := range e.Matching {
+ matching[i] = match.String()
+ }
+
+ return fmt.Sprintf("Multiple matching %s found:\n%s",
+ e.entityType,
+ strings.Join(matching, "\n"))
+}
diff --git a/entity/id.go b/entity/id.go
new file mode 100644
index 00000000..7ff6b223
--- /dev/null
+++ b/entity/id.go
@@ -0,0 +1,67 @@
+package entity
+
+import (
+ "fmt"
+ "io"
+ "strings"
+
+ "github.com/pkg/errors"
+)
+
+const IdLengthSHA1 = 40
+const IdLengthSHA256 = 64
+const humanIdLength = 7
+
+const UnsetId = Id("unset")
+
+// Id is an identifier for an entity or part of an entity
+type Id string
+
+// String return the identifier as a string
+func (i Id) String() string {
+ return string(i)
+}
+
+// Human return the identifier, shortened for human consumption
+func (i Id) Human() string {
+ format := fmt.Sprintf("%%.%ds", humanIdLength)
+ return fmt.Sprintf(format, i)
+}
+
+func (i Id) HasPrefix(prefix string) bool {
+ return strings.HasPrefix(string(i), prefix)
+}
+
+// UnmarshalGQL implement the Unmarshaler interface for gqlgen
+func (i *Id) UnmarshalGQL(v interface{}) error {
+ _, ok := v.(string)
+ if !ok {
+ return fmt.Errorf("IDs must be strings")
+ }
+
+ *i = v.(Id)
+
+ if err := i.Validate(); err != nil {
+ return errors.Wrap(err, "invalid ID")
+ }
+
+ return nil
+}
+
+// MarshalGQL implement the Marshaler interface for gqlgen
+func (i Id) MarshalGQL(w io.Writer) {
+ _, _ = w.Write([]byte(`"` + i.String() + `"`))
+}
+
+// IsValid tell if the Id is valid
+func (i Id) Validate() error {
+ if len(i) != IdLengthSHA1 && len(i) != IdLengthSHA256 {
+ return fmt.Errorf("invalid length")
+ }
+ for _, r := range i {
+ if (r < 'a' || r > 'z') && (r < '0' || r > '9') {
+ return fmt.Errorf("invalid character")
+ }
+ }
+ return nil
+}
diff --git a/entity/interface.go b/entity/interface.go
index 62b92a58..dd5d69b1 100644
--- a/entity/interface.go
+++ b/entity/interface.go
@@ -2,7 +2,5 @@ package entity
type Interface interface {
// Id return the Entity identifier
- Id() string
- // HumanId return the Entity identifier truncated for human consumption
- HumanId() string
+ Id() Id
}
diff --git a/entity/merge.go b/entity/merge.go
index 544f2168..7c3e71c8 100644
--- a/entity/merge.go
+++ b/entity/merge.go
@@ -1,6 +1,8 @@
package entity
-import "fmt"
+import (
+ "fmt"
+)
// MergeStatus represent the result of a merge operation of an entity
type MergeStatus int
@@ -17,7 +19,7 @@ type MergeResult struct {
// Err is set when a terminal error occur in the process
Err error
- Id string
+ Id Id
Status MergeStatus
// Only set for invalid status
@@ -42,14 +44,14 @@ func (mr MergeResult) String() string {
}
}
-func NewMergeError(err error, id string) MergeResult {
+func NewMergeError(err error, id Id) MergeResult {
return MergeResult{
Err: err,
Id: id,
}
}
-func NewMergeStatus(status MergeStatus, id string, entity Interface) MergeResult {
+func NewMergeStatus(status MergeStatus, id Id, entity Interface) MergeResult {
return MergeResult{
Id: id,
Status: status,
@@ -59,7 +61,7 @@ func NewMergeStatus(status MergeStatus, id string, entity Interface) MergeResult
}
}
-func NewMergeInvalidStatus(id string, reason string) MergeResult {
+func NewMergeInvalidStatus(id Id, reason string) MergeResult {
return MergeResult{
Id: id,
Status: MergeStatusInvalid,
diff --git a/graphql/connections/connections.go b/graphql/connections/connections.go
index d54a5068..d82c5620 100644
--- a/graphql/connections/connections.go
+++ b/graphql/connections/connections.go
@@ -1,5 +1,5 @@
-//go:generate genny -in=connection_template.go -out=gen_lazy_bug.go gen "Name=LazyBug NodeType=string EdgeType=LazyBugEdge ConnectionType=models.BugConnection"
-//go:generate genny -in=connection_template.go -out=gen_lazy_identity.go gen "Name=LazyIdentity NodeType=string EdgeType=LazyIdentityEdge ConnectionType=models.IdentityConnection"
+//go:generate genny -in=connection_template.go -out=gen_lazy_bug.go gen "Name=LazyBug NodeType=entity.Id EdgeType=LazyBugEdge ConnectionType=models.BugConnection"
+//go:generate genny -in=connection_template.go -out=gen_lazy_identity.go gen "Name=LazyIdentity NodeType=entity.Id EdgeType=LazyIdentityEdge ConnectionType=models.IdentityConnection"
//go:generate genny -in=connection_template.go -out=gen_identity.go gen "Name=Identity NodeType=identity.Interface EdgeType=models.IdentityEdge ConnectionType=models.IdentityConnection"
//go:generate genny -in=connection_template.go -out=gen_operation.go gen "Name=Operation NodeType=bug.Operation EdgeType=models.OperationEdge ConnectionType=models.OperationConnection"
//go:generate genny -in=connection_template.go -out=gen_comment.go gen "Name=Comment NodeType=bug.Comment EdgeType=models.CommentEdge ConnectionType=models.CommentConnection"
diff --git a/graphql/connections/gen_lazy_bug.go b/graphql/connections/gen_lazy_bug.go
index 6c9eb012..9638e86b 100644
--- a/graphql/connections/gen_lazy_bug.go
+++ b/graphql/connections/gen_lazy_bug.go
@@ -7,23 +7,24 @@ package connections
import (
"fmt"
+ "github.com/MichaelMure/git-bug/entity"
"github.com/MichaelMure/git-bug/graphql/models"
)
-// StringEdgeMaker define a function that take a string and an offset and
+// EntityIdEdgeMaker define a function that take a entity.Id and an offset and
// create an Edge.
-type LazyBugEdgeMaker func(value string, offset int) Edge
+type LazyBugEdgeMaker func(value entity.Id, offset int) Edge
// LazyBugConMaker define a function that create a models.BugConnection
type LazyBugConMaker func(
edges []*LazyBugEdge,
- nodes []string,
+ nodes []entity.Id,
info *models.PageInfo,
totalCount int) (*models.BugConnection, error)
// LazyBugCon will paginate a source according to the input of a relay connection
-func LazyBugCon(source []string, edgeMaker LazyBugEdgeMaker, conMaker LazyBugConMaker, input models.ConnectionInput) (*models.BugConnection, error) {
- var nodes []string
+func LazyBugCon(source []entity.Id, edgeMaker LazyBugEdgeMaker, conMaker LazyBugConMaker, input models.ConnectionInput) (*models.BugConnection, error) {
+ var nodes []entity.Id
var edges []*LazyBugEdge
var cursors []string
var pageInfo = &models.PageInfo{}
diff --git a/graphql/connections/gen_lazy_identity.go b/graphql/connections/gen_lazy_identity.go
index 96461be5..932d802c 100644
--- a/graphql/connections/gen_lazy_identity.go
+++ b/graphql/connections/gen_lazy_identity.go
@@ -7,23 +7,24 @@ package connections
import (
"fmt"
+ "github.com/MichaelMure/git-bug/entity"
"github.com/MichaelMure/git-bug/graphql/models"
)
-// StringEdgeMaker define a function that take a string and an offset and
+// EntityIdEdgeMaker define a function that take a entity.Id and an offset and
// create an Edge.
-type LazyIdentityEdgeMaker func(value string, offset int) Edge
+type LazyIdentityEdgeMaker func(value entity.Id, offset int) Edge
// LazyIdentityConMaker define a function that create a models.IdentityConnection
type LazyIdentityConMaker func(
edges []*LazyIdentityEdge,
- nodes []string,
+ nodes []entity.Id,
info *models.PageInfo,
totalCount int) (*models.IdentityConnection, error)
// LazyIdentityCon will paginate a source according to the input of a relay connection
-func LazyIdentityCon(source []string, edgeMaker LazyIdentityEdgeMaker, conMaker LazyIdentityConMaker, input models.ConnectionInput) (*models.IdentityConnection, error) {
- var nodes []string
+func LazyIdentityCon(source []entity.Id, edgeMaker LazyIdentityEdgeMaker, conMaker LazyIdentityConMaker, input models.ConnectionInput) (*models.IdentityConnection, error) {
+ var nodes []entity.Id
var edges []*LazyIdentityEdge
var cursors []string
var pageInfo = &models.PageInfo{}
diff --git a/graphql/connections/lazy_bug.go b/graphql/connections/lazy_bug.go
index 24eda0b6..00692e8b 100644
--- a/graphql/connections/lazy_bug.go
+++ b/graphql/connections/lazy_bug.go
@@ -1,8 +1,10 @@
package connections
+import "github.com/MichaelMure/git-bug/entity"
+
// LazyBugEdge is a special relay edge used to implement a lazy loading connection
type LazyBugEdge struct {
- Id string
+ Id entity.Id
Cursor string
}
diff --git a/graphql/connections/lazy_identity.go b/graphql/connections/lazy_identity.go
index 34ba579a..3274dd7e 100644
--- a/graphql/connections/lazy_identity.go
+++ b/graphql/connections/lazy_identity.go
@@ -1,8 +1,10 @@
package connections
+import "github.com/MichaelMure/git-bug/entity"
+
// LazyIdentityEdge is a special relay edge used to implement a lazy loading connection
type LazyIdentityEdge struct {
- Id string
+ Id entity.Id
Cursor string
}
diff --git a/graphql/graph/gen_graph.go b/graphql/graph/gen_graph.go
index d6d33e93..c052e57b 100644
--- a/graphql/graph/gen_graph.go
+++ b/graphql/graph/gen_graph.go
@@ -71,7 +71,7 @@ type ComplexityRoot struct {
Author func(childComplexity int) int
Date func(childComplexity int) int
Files func(childComplexity int) int
- Hash func(childComplexity int) int
+ ID func(childComplexity int) int
Message func(childComplexity int) int
}
@@ -86,8 +86,8 @@ type ComplexityRoot struct {
CreatedAt func(childComplexity int) int
Edited func(childComplexity int) int
Files func(childComplexity int) int
- Hash func(childComplexity int) int
History func(childComplexity int) int
+ ID func(childComplexity int) int
LastEdit func(childComplexity int) int
Message func(childComplexity int) int
MessageIsEmpty func(childComplexity int) int
@@ -98,8 +98,8 @@ type ComplexityRoot struct {
Author func(childComplexity int) int
Comments func(childComplexity int, after *string, before *string, first *int, last *int) int
CreatedAt func(childComplexity int) int
- HumanId func(childComplexity int) int
- Id func(childComplexity int) int
+ HumanID func(childComplexity int) int
+ ID func(childComplexity int) int
Labels func(childComplexity int) int
LastEdit func(childComplexity int) int
Operations func(childComplexity int, after *string, before *string, first *int, last *int) int
@@ -177,7 +177,7 @@ type ComplexityRoot struct {
Author func(childComplexity int) int
Date func(childComplexity int) int
Files func(childComplexity int) int
- Hash func(childComplexity int) int
+ ID func(childComplexity int) int
Message func(childComplexity int) int
Title func(childComplexity int) int
}
@@ -187,8 +187,8 @@ type ComplexityRoot struct {
CreatedAt func(childComplexity int) int
Edited func(childComplexity int) int
Files func(childComplexity int) int
- Hash func(childComplexity int) int
History func(childComplexity int) int
+ ID func(childComplexity int) int
LastEdit func(childComplexity int) int
Message func(childComplexity int) int
MessageIsEmpty func(childComplexity int) int
@@ -198,7 +198,7 @@ type ComplexityRoot struct {
Author func(childComplexity int) int
Date func(childComplexity int) int
Files func(childComplexity int) int
- Hash func(childComplexity int) int
+ ID func(childComplexity int) int
Message func(childComplexity int) int
Target func(childComplexity int) int
}
@@ -235,7 +235,7 @@ type ComplexityRoot struct {
Added func(childComplexity int) int
Author func(childComplexity int) int
Date func(childComplexity int) int
- Hash func(childComplexity int) int
+ ID func(childComplexity int) int
Removed func(childComplexity int) int
}
@@ -248,7 +248,7 @@ type ComplexityRoot struct {
Added func(childComplexity int) int
Author func(childComplexity int) int
Date func(childComplexity int) int
- Hash func(childComplexity int) int
+ ID func(childComplexity int) int
Removed func(childComplexity int) int
}
@@ -311,21 +311,21 @@ type ComplexityRoot struct {
SetStatusOperation struct {
Author func(childComplexity int) int
Date func(childComplexity int) int
- Hash func(childComplexity int) int
+ ID func(childComplexity int) int
Status func(childComplexity int) int
}
SetStatusTimelineItem struct {
Author func(childComplexity int) int
Date func(childComplexity int) int
- Hash func(childComplexity int) int
+ ID func(childComplexity int) int
Status func(childComplexity int) int
}
SetTitleOperation struct {
Author func(childComplexity int) int
Date func(childComplexity int) int
- Hash func(childComplexity int) int
+ ID func(childComplexity int) int
Title func(childComplexity int) int
Was func(childComplexity int) int
}
@@ -339,7 +339,7 @@ type ComplexityRoot struct {
SetTitleTimelineItem struct {
Author func(childComplexity int) int
Date func(childComplexity int) int
- Hash func(childComplexity int) int
+ ID func(childComplexity int) int
Title func(childComplexity int) int
Was func(childComplexity int) int
}
@@ -358,13 +358,19 @@ type ComplexityRoot struct {
}
type AddCommentOperationResolver interface {
+ ID(ctx context.Context, obj *bug.AddCommentOperation) (string, error)
+
Date(ctx context.Context, obj *bug.AddCommentOperation) (*time.Time, error)
}
type AddCommentTimelineItemResolver interface {
+ ID(ctx context.Context, obj *bug.AddCommentTimelineItem) (string, error)
+
CreatedAt(ctx context.Context, obj *bug.AddCommentTimelineItem) (*time.Time, error)
LastEdit(ctx context.Context, obj *bug.AddCommentTimelineItem) (*time.Time, error)
}
type BugResolver interface {
+ ID(ctx context.Context, obj *bug.Snapshot) (string, error)
+ HumanID(ctx context.Context, obj *bug.Snapshot) (string, error)
Status(ctx context.Context, obj *bug.Snapshot) (models.Status, error)
LastEdit(ctx context.Context, obj *bug.Snapshot) (*time.Time, error)
@@ -383,14 +389,21 @@ type CommentHistoryStepResolver interface {
Date(ctx context.Context, obj *bug.CommentHistoryStep) (*time.Time, error)
}
type CreateOperationResolver interface {
+ ID(ctx context.Context, obj *bug.CreateOperation) (string, error)
+
Date(ctx context.Context, obj *bug.CreateOperation) (*time.Time, error)
}
type CreateTimelineItemResolver interface {
+ ID(ctx context.Context, obj *bug.CreateTimelineItem) (string, error)
+
CreatedAt(ctx context.Context, obj *bug.CreateTimelineItem) (*time.Time, error)
LastEdit(ctx context.Context, obj *bug.CreateTimelineItem) (*time.Time, error)
}
type EditCommentOperationResolver interface {
+ ID(ctx context.Context, obj *bug.EditCommentOperation) (string, error)
+
Date(ctx context.Context, obj *bug.EditCommentOperation) (*time.Time, error)
+ Target(ctx context.Context, obj *bug.EditCommentOperation) (string, error)
}
type IdentityResolver interface {
ID(ctx context.Context, obj *identity.Interface) (string, error)
@@ -407,12 +420,16 @@ type LabelResolver interface {
Color(ctx context.Context, obj *bug.Label) (*color.RGBA, error)
}
type LabelChangeOperationResolver interface {
+ ID(ctx context.Context, obj *bug.LabelChangeOperation) (string, error)
+
Date(ctx context.Context, obj *bug.LabelChangeOperation) (*time.Time, error)
}
type LabelChangeResultResolver interface {
Status(ctx context.Context, obj *bug.LabelChangeResult) (models.LabelChangeStatus, error)
}
type LabelChangeTimelineItemResolver interface {
+ ID(ctx context.Context, obj *bug.LabelChangeTimelineItem) (string, error)
+
Date(ctx context.Context, obj *bug.LabelChangeTimelineItem) (*time.Time, error)
}
type MutationResolver interface {
@@ -438,17 +455,25 @@ type RepositoryResolver interface {
ValidLabels(ctx context.Context, obj *models.Repository) ([]bug.Label, error)
}
type SetStatusOperationResolver interface {
+ ID(ctx context.Context, obj *bug.SetStatusOperation) (string, error)
+
Date(ctx context.Context, obj *bug.SetStatusOperation) (*time.Time, error)
Status(ctx context.Context, obj *bug.SetStatusOperation) (models.Status, error)
}
type SetStatusTimelineItemResolver interface {
+ ID(ctx context.Context, obj *bug.SetStatusTimelineItem) (string, error)
+
Date(ctx context.Context, obj *bug.SetStatusTimelineItem) (*time.Time, error)
Status(ctx context.Context, obj *bug.SetStatusTimelineItem) (models.Status, error)
}
type SetTitleOperationResolver interface {
+ ID(ctx context.Context, obj *bug.SetTitleOperation) (string, error)
+
Date(ctx context.Context, obj *bug.SetTitleOperation) (*time.Time, error)
}
type SetTitleTimelineItemResolver interface {
+ ID(ctx context.Context, obj *bug.SetTitleTimelineItem) (string, error)
+
Date(ctx context.Context, obj *bug.SetTitleTimelineItem) (*time.Time, error)
}
@@ -488,12 +513,12 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.AddCommentOperation.Files(childComplexity), true
- case "AddCommentOperation.hash":
- if e.complexity.AddCommentOperation.Hash == nil {
+ case "AddCommentOperation.id":
+ if e.complexity.AddCommentOperation.ID == nil {
break
}
- return e.complexity.AddCommentOperation.Hash(childComplexity), true
+ return e.complexity.AddCommentOperation.ID(childComplexity), true
case "AddCommentOperation.message":
if e.complexity.AddCommentOperation.Message == nil {
@@ -551,19 +576,19 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.AddCommentTimelineItem.Files(childComplexity), true
- case "AddCommentTimelineItem.hash":
- if e.complexity.AddCommentTimelineItem.Hash == nil {
+ case "AddCommentTimelineItem.history":
+ if e.complexity.AddCommentTimelineItem.History == nil {
break
}
- return e.complexity.AddCommentTimelineItem.Hash(childComplexity), true
+ return e.complexity.AddCommentTimelineItem.History(childComplexity), true
- case "AddCommentTimelineItem.history":
- if e.complexity.AddCommentTimelineItem.History == nil {
+ case "AddCommentTimelineItem.id":
+ if e.complexity.AddCommentTimelineItem.ID == nil {
break
}
- return e.complexity.AddCommentTimelineItem.History(childComplexity), true
+ return e.complexity.AddCommentTimelineItem.ID(childComplexity), true
case "AddCommentTimelineItem.lastEdit":
if e.complexity.AddCommentTimelineItem.LastEdit == nil {
@@ -625,18 +650,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.Bug.CreatedAt(childComplexity), true
case "Bug.humanId":
- if e.complexity.Bug.HumanId == nil {
+ if e.complexity.Bug.HumanID == nil {
break
}
- return e.complexity.Bug.HumanId(childComplexity), true
+ return e.complexity.Bug.HumanID(childComplexity), true
case "Bug.id":
- if e.complexity.Bug.Id == nil {
+ if e.complexity.Bug.ID == nil {
break
}
- return e.complexity.Bug.Id(childComplexity), true
+ return e.complexity.Bug.ID(childComplexity), true
case "Bug.labels":
if e.complexity.Bug.Labels == nil {
@@ -940,12 +965,12 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.CreateOperation.Files(childComplexity), true
- case "CreateOperation.hash":
- if e.complexity.CreateOperation.Hash == nil {
+ case "CreateOperation.id":
+ if e.complexity.CreateOperation.ID == nil {
break
}
- return e.complexity.CreateOperation.Hash(childComplexity), true
+ return e.complexity.CreateOperation.ID(childComplexity), true
case "CreateOperation.message":
if e.complexity.CreateOperation.Message == nil {
@@ -989,19 +1014,19 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.CreateTimelineItem.Files(childComplexity), true
- case "CreateTimelineItem.hash":
- if e.complexity.CreateTimelineItem.Hash == nil {
+ case "CreateTimelineItem.history":
+ if e.complexity.CreateTimelineItem.History == nil {
break
}
- return e.complexity.CreateTimelineItem.Hash(childComplexity), true
+ return e.complexity.CreateTimelineItem.History(childComplexity), true
- case "CreateTimelineItem.history":
- if e.complexity.CreateTimelineItem.History == nil {
+ case "CreateTimelineItem.id":
+ if e.complexity.CreateTimelineItem.ID == nil {
break
}
- return e.complexity.CreateTimelineItem.History(childComplexity), true
+ return e.complexity.CreateTimelineItem.ID(childComplexity), true
case "CreateTimelineItem.lastEdit":
if e.complexity.CreateTimelineItem.LastEdit == nil {
@@ -1045,12 +1070,12 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.EditCommentOperation.Files(childComplexity), true
- case "EditCommentOperation.hash":
- if e.complexity.EditCommentOperation.Hash == nil {
+ case "EditCommentOperation.id":
+ if e.complexity.EditCommentOperation.ID == nil {
break
}
- return e.complexity.EditCommentOperation.Hash(childComplexity), true
+ return e.complexity.EditCommentOperation.ID(childComplexity), true
case "EditCommentOperation.message":
if e.complexity.EditCommentOperation.Message == nil {
@@ -1199,12 +1224,12 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.LabelChangeOperation.Date(childComplexity), true
- case "LabelChangeOperation.hash":
- if e.complexity.LabelChangeOperation.Hash == nil {
+ case "LabelChangeOperation.id":
+ if e.complexity.LabelChangeOperation.ID == nil {
break
}
- return e.complexity.LabelChangeOperation.Hash(childComplexity), true
+ return e.complexity.LabelChangeOperation.ID(childComplexity), true
case "LabelChangeOperation.removed":
if e.complexity.LabelChangeOperation.Removed == nil {
@@ -1248,12 +1273,12 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.LabelChangeTimelineItem.Date(childComplexity), true
- case "LabelChangeTimelineItem.hash":
- if e.complexity.LabelChangeTimelineItem.Hash == nil {
+ case "LabelChangeTimelineItem.id":
+ if e.complexity.LabelChangeTimelineItem.ID == nil {
break
}
- return e.complexity.LabelChangeTimelineItem.Hash(childComplexity), true
+ return e.complexity.LabelChangeTimelineItem.ID(childComplexity), true
case "LabelChangeTimelineItem.removed":
if e.complexity.LabelChangeTimelineItem.Removed == nil {
@@ -1565,12 +1590,12 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.SetStatusOperation.Date(childComplexity), true
- case "SetStatusOperation.hash":
- if e.complexity.SetStatusOperation.Hash == nil {
+ case "SetStatusOperation.id":
+ if e.complexity.SetStatusOperation.ID == nil {
break
}
- return e.complexity.SetStatusOperation.Hash(childComplexity), true
+ return e.complexity.SetStatusOperation.ID(childComplexity), true
case "SetStatusOperation.status":
if e.complexity.SetStatusOperation.Status == nil {
@@ -1593,12 +1618,12 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.SetStatusTimelineItem.Date(childComplexity), true
- case "SetStatusTimelineItem.hash":
- if e.complexity.SetStatusTimelineItem.Hash == nil {
+ case "SetStatusTimelineItem.id":
+ if e.complexity.SetStatusTimelineItem.ID == nil {
break
}
- return e.complexity.SetStatusTimelineItem.Hash(childComplexity), true
+ return e.complexity.SetStatusTimelineItem.ID(childComplexity), true
case "SetStatusTimelineItem.status":
if e.complexity.SetStatusTimelineItem.Status == nil {
@@ -1621,12 +1646,12 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.SetTitleOperation.Date(childComplexity), true
- case "SetTitleOperation.hash":
- if e.complexity.SetTitleOperation.Hash == nil {
+ case "SetTitleOperation.id":
+ if e.complexity.SetTitleOperation.ID == nil {
break
}
- return e.complexity.SetTitleOperation.Hash(childComplexity), true
+ return e.complexity.SetTitleOperation.ID(childComplexity), true
case "SetTitleOperation.title":
if e.complexity.SetTitleOperation.Title == nil {
@@ -1677,12 +1702,12 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.SetTitleTimelineItem.Date(childComplexity), true
- case "SetTitleTimelineItem.hash":
- if e.complexity.SetTitleTimelineItem.Hash == nil {
+ case "SetTitleTimelineItem.id":
+ if e.complexity.SetTitleTimelineItem.ID == nil {
break
}
- return e.complexity.SetTitleTimelineItem.Hash(childComplexity), true
+ return e.complexity.SetTitleTimelineItem.ID(childComplexity), true
case "SetTitleTimelineItem.title":
if e.complexity.SetTitleTimelineItem.Title == nil {
@@ -2126,8 +2151,8 @@ type CommitAsNeededPayload {
`},
&ast.Source{Name: "schema/operations.graphql", Input: `"""An operation applied to a bug."""
interface Operation {
- """The hash of the operation"""
- hash: Hash!
+ """The identifier of the operation"""
+ id: String!
"""The operations author."""
author: Identity!
"""The datetime when this operation was issued."""
@@ -2153,8 +2178,8 @@ type OperationEdge {
# Operations
type CreateOperation implements Operation & Authored {
- """The hash of the operation"""
- hash: Hash!
+ """The identifier of the operation"""
+ id: String!
"""The author of this object."""
author: Identity!
"""The datetime when this operation was issued."""
@@ -2166,8 +2191,8 @@ type CreateOperation implements Operation & Authored {
}
type SetTitleOperation implements Operation & Authored {
- """The hash of the operation"""
- hash: Hash!
+ """The identifier of the operation"""
+ id: String!
"""The author of this object."""
author: Identity!
"""The datetime when this operation was issued."""
@@ -2178,8 +2203,8 @@ type SetTitleOperation implements Operation & Authored {
}
type AddCommentOperation implements Operation & Authored {
- """The hash of the operation"""
- hash: Hash!
+ """The identifier of the operation"""
+ id: String!
"""The author of this object."""
author: Identity!
"""The datetime when this operation was issued."""
@@ -2190,21 +2215,21 @@ type AddCommentOperation implements Operation & Authored {
}
type EditCommentOperation implements Operation & Authored {
- """The hash of the operation"""
- hash: Hash!
+ """The identifier of the operation"""
+ id: String!
"""The author of this object."""
author: Identity!
"""The datetime when this operation was issued."""
date: Time!
- target: Hash!
+ target: String!
message: String!
files: [Hash!]!
}
type SetStatusOperation implements Operation & Authored {
- """The hash of the operation"""
- hash: Hash!
+ """The identifier of the operation"""
+ id: String!
"""The author of this object."""
author: Identity!
"""The datetime when this operation was issued."""
@@ -2214,8 +2239,8 @@ type SetStatusOperation implements Operation & Authored {
}
type LabelChangeOperation implements Operation & Authored {
- """The hash of the operation"""
- hash: Hash!
+ """The identifier of the operation"""
+ id: String!
"""The author of this object."""
author: Identity!
"""The datetime when this operation was issued."""
@@ -2291,8 +2316,8 @@ type Mutation {
`},
&ast.Source{Name: "schema/timeline.graphql", Input: `"""An item in the timeline of events"""
interface TimelineItem {
- """The hash of the source operation"""
- hash: Hash!
+ """The identifier of the source operation"""
+ id: String!
}
"""CommentHistoryStep hold one version of a message in the history"""
@@ -2321,8 +2346,8 @@ type TimelineItemEdge {
"""CreateTimelineItem is a TimelineItem that represent the creation of a bug and its message edition history"""
type CreateTimelineItem implements TimelineItem & Authored {
- """The hash of the source operation"""
- hash: Hash!
+ """The identifier of the source operation"""
+ id: String!
author: Identity!
message: String!
messageIsEmpty: Boolean!
@@ -2335,8 +2360,8 @@ type CreateTimelineItem implements TimelineItem & Authored {
"""AddCommentTimelineItem is a TimelineItem that represent a Comment and its edition history"""
type AddCommentTimelineItem implements TimelineItem & Authored {
- """The hash of the source operation"""
- hash: Hash!
+ """The identifier of the source operation"""
+ id: String!
author: Identity!
message: String!
messageIsEmpty: Boolean!
@@ -2349,8 +2374,8 @@ type AddCommentTimelineItem implements TimelineItem & Authored {
"""LabelChangeTimelineItem is a TimelineItem that represent a change in the labels of a bug"""
type LabelChangeTimelineItem implements TimelineItem & Authored {
- """The hash of the source operation"""
- hash: Hash!
+ """The identifier of the source operation"""
+ id: String!
author: Identity!
date: Time!
added: [Label!]!
@@ -2359,8 +2384,8 @@ type LabelChangeTimelineItem implements TimelineItem & Authored {
"""SetStatusTimelineItem is a TimelineItem that represent a change in the status of a bug"""
type SetStatusTimelineItem implements TimelineItem & Authored {
- """The hash of the source operation"""
- hash: Hash!
+ """The identifier of the source operation"""
+ id: String!
author: Identity!
date: Time!
status: Status!
@@ -2368,8 +2393,8 @@ type SetStatusTimelineItem implements TimelineItem & Authored {
"""LabelChangeTimelineItem is a TimelineItem that represent a change in the title of a bug"""
type SetTitleTimelineItem implements TimelineItem & Authored {
- """The hash of the source operation"""
- hash: Hash!
+ """The identifier of the source operation"""
+ id: String!
author: Identity!
date: Time!
title: String!
@@ -2899,7 +2924,7 @@ func (ec *executionContext) field___Type_fields_args(ctx context.Context, rawArg
// region **************************** field.gotpl *****************************
-func (ec *executionContext) _AddCommentOperation_hash(ctx context.Context, field graphql.CollectedField, obj *bug.AddCommentOperation) (ret graphql.Marshaler) {
+func (ec *executionContext) _AddCommentOperation_id(ctx context.Context, field graphql.CollectedField, obj *bug.AddCommentOperation) (ret graphql.Marshaler) {
ctx = ec.Tracer.StartFieldExecution(ctx, field)
defer func() {
if r := recover(); r != nil {
@@ -2918,7 +2943,7 @@ func (ec *executionContext) _AddCommentOperation_hash(ctx context.Context, field
ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx)
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
- return obj.Hash()
+ return ec.resolvers.AddCommentOperation().ID(rctx, obj)
})
if err != nil {
ec.Error(ctx, err)
@@ -2930,10 +2955,10 @@ func (ec *executionContext) _AddCommentOperation_hash(ctx context.Context, field
}
return graphql.Null
}
- res := resTmp.(git.Hash)
+ res := resTmp.(string)
rctx.Result = res
ctx = ec.Tracer.StartFieldChildExecution(ctx)
- return ec.marshalNHash2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋutilᚋgitᚐHash(ctx, field.Selections, res)
+ return ec.marshalNString2string(ctx, field.Selections, res)
}
func (ec *executionContext) _AddCommentOperation_author(ctx context.Context, field graphql.CollectedField, obj *bug.AddCommentOperation) (ret graphql.Marshaler) {
@@ -3192,7 +3217,7 @@ func (ec *executionContext) _AddCommentPayload_operation(ctx context.Context, fi
return ec.marshalNAddCommentOperation2ᚖgithubᚗcomᚋMichaelMureᚋgitᚑbugᚋbugᚐAddCommentOperation(ctx, field.Selections, res)
}
-func (ec *executionContext) _AddCommentTimelineItem_hash(ctx context.Context, field graphql.CollectedField, obj *bug.AddCommentTimelineItem) (ret graphql.Marshaler) {
+func (ec *executionContext) _AddCommentTimelineItem_id(ctx context.Context, field graphql.CollectedField, obj *bug.AddCommentTimelineItem) (ret graphql.Marshaler) {
ctx = ec.Tracer.StartFieldExecution(ctx, field)
defer func() {
if r := recover(); r != nil {
@@ -3211,7 +3236,7 @@ func (ec *executionContext) _AddCommentTimelineItem_hash(ctx context.Context, fi
ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx)
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
- return obj.Hash(), nil
+ return ec.resolvers.AddCommentTimelineItem().ID(rctx, obj)
})
if err != nil {
ec.Error(ctx, err)
@@ -3223,10 +3248,10 @@ func (ec *executionContext) _AddCommentTimelineItem_hash(ctx context.Context, fi
}
return graphql.Null
}
- res := resTmp.(git.Hash)
+ res := resTmp.(string)
rctx.Result = res
ctx = ec.Tracer.StartFieldChildExecution(ctx)
- return ec.marshalNHash2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋutilᚋgitᚐHash(ctx, field.Selections, res)
+ return ec.marshalNString2string(ctx, field.Selections, res)
}
func (ec *executionContext) _AddCommentTimelineItem_author(ctx context.Context, field graphql.CollectedField, obj *bug.AddCommentTimelineItem) (ret graphql.Marshaler) {
@@ -3544,7 +3569,7 @@ func (ec *executionContext) _Bug_id(ctx context.Context, field graphql.Collected
ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx)
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
- return obj.Id(), nil
+ return ec.resolvers.Bug().ID(rctx, obj)
})
if err != nil {
ec.Error(ctx, err)
@@ -3581,7 +3606,7 @@ func (ec *executionContext) _Bug_humanId(ctx context.Context, field graphql.Coll
ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx)
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
- return obj.HumanId(), nil
+ return ec.resolvers.Bug().HumanID(rctx, obj)
})
if err != nil {
ec.Error(ctx, err)
@@ -5176,7 +5201,7 @@ func (ec *executionContext) _CommitPayload_bug(ctx context.Context, field graphq
return ec.marshalNBug2ᚖgithubᚗcomᚋMichaelMureᚋgitᚑbugᚋbugᚐSnapshot(ctx, field.Selections, res)
}
-func (ec *executionContext) _CreateOperation_hash(ctx context.Context, field graphql.CollectedField, obj *bug.CreateOperation) (ret graphql.Marshaler) {
+func (ec *executionContext) _CreateOperation_id(ctx context.Context, field graphql.CollectedField, obj *bug.CreateOperation) (ret graphql.Marshaler) {
ctx = ec.Tracer.StartFieldExecution(ctx, field)
defer func() {
if r := recover(); r != nil {
@@ -5195,7 +5220,7 @@ func (ec *executionContext) _CreateOperation_hash(ctx context.Context, field gra
ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx)
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
- return obj.Hash()
+ return ec.resolvers.CreateOperation().ID(rctx, obj)
})
if err != nil {
ec.Error(ctx, err)
@@ -5207,10 +5232,10 @@ func (ec *executionContext) _CreateOperation_hash(ctx context.Context, field gra
}
return graphql.Null
}
- res := resTmp.(git.Hash)
+ res := resTmp.(string)
rctx.Result = res
ctx = ec.Tracer.StartFieldChildExecution(ctx)
- return ec.marshalNHash2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋutilᚋgitᚐHash(ctx, field.Selections, res)
+ return ec.marshalNString2string(ctx, field.Selections, res)
}
func (ec *executionContext) _CreateOperation_author(ctx context.Context, field graphql.CollectedField, obj *bug.CreateOperation) (ret graphql.Marshaler) {
@@ -5398,7 +5423,7 @@ func (ec *executionContext) _CreateOperation_files(ctx context.Context, field gr
return ec.marshalNHash2ᚕgithubᚗcomᚋMichaelMureᚋgitᚑbugᚋutilᚋgitᚐHash(ctx, field.Selections, res)
}
-func (ec *executionContext) _CreateTimelineItem_hash(ctx context.Context, field graphql.CollectedField, obj *bug.CreateTimelineItem) (ret graphql.Marshaler) {
+func (ec *executionContext) _CreateTimelineItem_id(ctx context.Context, field graphql.CollectedField, obj *bug.CreateTimelineItem) (ret graphql.Marshaler) {
ctx = ec.Tracer.StartFieldExecution(ctx, field)
defer func() {
if r := recover(); r != nil {
@@ -5417,7 +5442,7 @@ func (ec *executionContext) _CreateTimelineItem_hash(ctx context.Context, field
ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx)
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
- return obj.Hash(), nil
+ return ec.resolvers.CreateTimelineItem().ID(rctx, obj)
})
if err != nil {
ec.Error(ctx, err)
@@ -5429,10 +5454,10 @@ func (ec *executionContext) _CreateTimelineItem_hash(ctx context.Context, field
}
return graphql.Null
}
- res := resTmp.(git.Hash)
+ res := resTmp.(string)
rctx.Result = res
ctx = ec.Tracer.StartFieldChildExecution(ctx)
- return ec.marshalNHash2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋutilᚋgitᚐHash(ctx, field.Selections, res)
+ return ec.marshalNString2string(ctx, field.Selections, res)
}
func (ec *executionContext) _CreateTimelineItem_author(ctx context.Context, field graphql.CollectedField, obj *bug.CreateTimelineItem) (ret graphql.Marshaler) {
@@ -5731,7 +5756,7 @@ func (ec *executionContext) _CreateTimelineItem_history(ctx context.Context, fie
return ec.marshalNCommentHistoryStep2ᚕgithubᚗcomᚋMichaelMureᚋgitᚑbugᚋbugᚐCommentHistoryStep(ctx, field.Selections, res)
}
-func (ec *executionContext) _EditCommentOperation_hash(ctx context.Context, field graphql.CollectedField, obj *bug.EditCommentOperation) (ret graphql.Marshaler) {
+func (ec *executionContext) _EditCommentOperation_id(ctx context.Context, field graphql.CollectedField, obj *bug.EditCommentOperation) (ret graphql.Marshaler) {
ctx = ec.Tracer.StartFieldExecution(ctx, field)
defer func() {
if r := recover(); r != nil {
@@ -5750,7 +5775,7 @@ func (ec *executionContext) _EditCommentOperation_hash(ctx context.Context, fiel
ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx)
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
- return obj.Hash()
+ return ec.resolvers.EditCommentOperation().ID(rctx, obj)
})
if err != nil {
ec.Error(ctx, err)
@@ -5762,10 +5787,10 @@ func (ec *executionContext) _EditCommentOperation_hash(ctx context.Context, fiel
}
return graphql.Null
}
- res := resTmp.(git.Hash)
+ res := resTmp.(string)
rctx.Result = res
ctx = ec.Tracer.StartFieldChildExecution(ctx)
- return ec.marshalNHash2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋutilᚋgitᚐHash(ctx, field.Selections, res)
+ return ec.marshalNString2string(ctx, field.Selections, res)
}
func (ec *executionContext) _EditCommentOperation_author(ctx context.Context, field graphql.CollectedField, obj *bug.EditCommentOperation) (ret graphql.Marshaler) {
@@ -5855,13 +5880,13 @@ func (ec *executionContext) _EditCommentOperation_target(ctx context.Context, fi
Object: "EditCommentOperation",
Field: field,
Args: nil,
- IsMethod: false,
+ IsMethod: true,
}
ctx = graphql.WithResolverContext(ctx, rctx)
ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx)
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
- return obj.Target, nil
+ return ec.resolvers.EditCommentOperation().Target(rctx, obj)
})
if err != nil {
ec.Error(ctx, err)
@@ -5873,10 +5898,10 @@ func (ec *executionContext) _EditCommentOperation_target(ctx context.Context, fi
}
return graphql.Null
}
- res := resTmp.(git.Hash)
+ res := resTmp.(string)
rctx.Result = res
ctx = ec.Tracer.StartFieldChildExecution(ctx)
- return ec.marshalNHash2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋutilᚋgitᚐHash(ctx, field.Selections, res)
+ return ec.marshalNString2string(ctx, field.Selections, res)
}
func (ec *executionContext) _EditCommentOperation_message(ctx context.Context, field graphql.CollectedField, obj *bug.EditCommentOperation) (ret graphql.Marshaler) {
@@ -6533,7 +6558,7 @@ func (ec *executionContext) _Label_color(ctx context.Context, field graphql.Coll
return ec.marshalNColor2ᚖimageᚋcolorᚐRGBA(ctx, field.Selections, res)
}
-func (ec *executionContext) _LabelChangeOperation_hash(ctx context.Context, field graphql.CollectedField, obj *bug.LabelChangeOperation) (ret graphql.Marshaler) {
+func (ec *executionContext) _LabelChangeOperation_id(ctx context.Context, field graphql.CollectedField, obj *bug.LabelChangeOperation) (ret graphql.Marshaler) {
ctx = ec.Tracer.StartFieldExecution(ctx, field)
defer func() {
if r := recover(); r != nil {
@@ -6552,7 +6577,7 @@ func (ec *executionContext) _LabelChangeOperation_hash(ctx context.Context, fiel
ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx)
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
- return obj.Hash()
+ return ec.resolvers.LabelChangeOperation().ID(rctx, obj)
})
if err != nil {
ec.Error(ctx, err)
@@ -6564,10 +6589,10 @@ func (ec *executionContext) _LabelChangeOperation_hash(ctx context.Context, fiel
}
return graphql.Null
}
- res := resTmp.(git.Hash)
+ res := resTmp.(string)
rctx.Result = res
ctx = ec.Tracer.StartFieldChildExecution(ctx)
- return ec.marshalNHash2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋutilᚋgitᚐHash(ctx, field.Selections, res)
+ return ec.marshalNString2string(ctx, field.Selections, res)
}
func (ec *executionContext) _LabelChangeOperation_author(ctx context.Context, field graphql.CollectedField, obj *bug.LabelChangeOperation) (ret graphql.Marshaler) {
@@ -6792,7 +6817,7 @@ func (ec *executionContext) _LabelChangeResult_status(ctx context.Context, field
return ec.marshalNLabelChangeStatus2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋgraphqlᚋmodelsᚐLabelChangeStatus(ctx, field.Selections, res)
}
-func (ec *executionContext) _LabelChangeTimelineItem_hash(ctx context.Context, field graphql.CollectedField, obj *bug.LabelChangeTimelineItem) (ret graphql.Marshaler) {
+func (ec *executionContext) _LabelChangeTimelineItem_id(ctx context.Context, field graphql.CollectedField, obj *bug.LabelChangeTimelineItem) (ret graphql.Marshaler) {
ctx = ec.Tracer.StartFieldExecution(ctx, field)
defer func() {
if r := recover(); r != nil {
@@ -6811,7 +6836,7 @@ func (ec *executionContext) _LabelChangeTimelineItem_hash(ctx context.Context, f
ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx)
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
- return obj.Hash(), nil
+ return ec.resolvers.LabelChangeTimelineItem().ID(rctx, obj)
})
if err != nil {
ec.Error(ctx, err)
@@ -6823,10 +6848,10 @@ func (ec *executionContext) _LabelChangeTimelineItem_hash(ctx context.Context, f
}
return graphql.Null
}
- res := resTmp.(git.Hash)
+ res := resTmp.(string)
rctx.Result = res
ctx = ec.Tracer.StartFieldChildExecution(ctx)
- return ec.marshalNHash2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋutilᚋgitᚐHash(ctx, field.Selections, res)
+ return ec.marshalNString2string(ctx, field.Selections, res)
}
func (ec *executionContext) _LabelChangeTimelineItem_author(ctx context.Context, field graphql.CollectedField, obj *bug.LabelChangeTimelineItem) (ret graphql.Marshaler) {
@@ -8306,7 +8331,7 @@ func (ec *executionContext) _Repository_validLabels(ctx context.Context, field g
return ec.marshalNLabel2ᚕgithubᚗcomᚋMichaelMureᚋgitᚑbugᚋbugᚐLabel(ctx, field.Selections, res)
}
-func (ec *executionContext) _SetStatusOperation_hash(ctx context.Context, field graphql.CollectedField, obj *bug.SetStatusOperation) (ret graphql.Marshaler) {
+func (ec *executionContext) _SetStatusOperation_id(ctx context.Context, field graphql.CollectedField, obj *bug.SetStatusOperation) (ret graphql.Marshaler) {
ctx = ec.Tracer.StartFieldExecution(ctx, field)
defer func() {
if r := recover(); r != nil {
@@ -8325,7 +8350,7 @@ func (ec *executionContext) _SetStatusOperation_hash(ctx context.Context, field
ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx)
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
- return obj.Hash()
+ return ec.resolvers.SetStatusOperation().ID(rctx, obj)
})
if err != nil {
ec.Error(ctx, err)
@@ -8337,10 +8362,10 @@ func (ec *executionContext) _SetStatusOperation_hash(ctx context.Context, field
}
return graphql.Null
}
- res := resTmp.(git.Hash)
+ res := resTmp.(string)
rctx.Result = res
ctx = ec.Tracer.StartFieldChildExecution(ctx)
- return ec.marshalNHash2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋutilᚋgitᚐHash(ctx, field.Selections, res)
+ return ec.marshalNString2string(ctx, field.Selections, res)
}
func (ec *executionContext) _SetStatusOperation_author(ctx context.Context, field graphql.CollectedField, obj *bug.SetStatusOperation) (ret graphql.Marshaler) {
@@ -8454,7 +8479,7 @@ func (ec *executionContext) _SetStatusOperation_status(ctx context.Context, fiel
return ec.marshalNStatus2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋgraphqlᚋmodelsᚐStatus(ctx, field.Selections, res)
}
-func (ec *executionContext) _SetStatusTimelineItem_hash(ctx context.Context, field graphql.CollectedField, obj *bug.SetStatusTimelineItem) (ret graphql.Marshaler) {
+func (ec *executionContext) _SetStatusTimelineItem_id(ctx context.Context, field graphql.CollectedField, obj *bug.SetStatusTimelineItem) (ret graphql.Marshaler) {
ctx = ec.Tracer.StartFieldExecution(ctx, field)
defer func() {
if r := recover(); r != nil {
@@ -8473,7 +8498,7 @@ func (ec *executionContext) _SetStatusTimelineItem_hash(ctx context.Context, fie
ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx)
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
- return obj.Hash(), nil
+ return ec.resolvers.SetStatusTimelineItem().ID(rctx, obj)
})
if err != nil {
ec.Error(ctx, err)
@@ -8485,10 +8510,10 @@ func (ec *executionContext) _SetStatusTimelineItem_hash(ctx context.Context, fie
}
return graphql.Null
}
- res := resTmp.(git.Hash)
+ res := resTmp.(string)
rctx.Result = res
ctx = ec.Tracer.StartFieldChildExecution(ctx)
- return ec.marshalNHash2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋutilᚋgitᚐHash(ctx, field.Selections, res)
+ return ec.marshalNString2string(ctx, field.Selections, res)
}
func (ec *executionContext) _SetStatusTimelineItem_author(ctx context.Context, field graphql.CollectedField, obj *bug.SetStatusTimelineItem) (ret graphql.Marshaler) {
@@ -8602,7 +8627,7 @@ func (ec *executionContext) _SetStatusTimelineItem_status(ctx context.Context, f
return ec.marshalNStatus2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋgraphqlᚋmodelsᚐStatus(ctx, field.Selections, res)
}
-func (ec *executionContext) _SetTitleOperation_hash(ctx context.Context, field graphql.CollectedField, obj *bug.SetTitleOperation) (ret graphql.Marshaler) {
+func (ec *executionContext) _SetTitleOperation_id(ctx context.Context, field graphql.CollectedField, obj *bug.SetTitleOperation) (ret graphql.Marshaler) {
ctx = ec.Tracer.StartFieldExecution(ctx, field)
defer func() {
if r := recover(); r != nil {
@@ -8621,7 +8646,7 @@ func (ec *executionContext) _SetTitleOperation_hash(ctx context.Context, field g
ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx)
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
- return obj.Hash()
+ return ec.resolvers.SetTitleOperation().ID(rctx, obj)
})
if err != nil {
ec.Error(ctx, err)
@@ -8633,10 +8658,10 @@ func (ec *executionContext) _SetTitleOperation_hash(ctx context.Context, field g
}
return graphql.Null
}
- res := resTmp.(git.Hash)
+ res := resTmp.(string)
rctx.Result = res
ctx = ec.Tracer.StartFieldChildExecution(ctx)
- return ec.marshalNHash2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋutilᚋgitᚐHash(ctx, field.Selections, res)
+ return ec.marshalNString2string(ctx, field.Selections, res)
}
func (ec *executionContext) _SetTitleOperation_author(ctx context.Context, field graphql.CollectedField, obj *bug.SetTitleOperation) (ret graphql.Marshaler) {
@@ -8895,7 +8920,7 @@ func (ec *executionContext) _SetTitlePayload_operation(ctx context.Context, fiel
return ec.marshalNSetTitleOperation2ᚖgithubᚗcomᚋMichaelMureᚋgitᚑbugᚋbugᚐSetTitleOperation(ctx, field.Selections, res)
}
-func (ec *executionContext) _SetTitleTimelineItem_hash(ctx context.Context, field graphql.CollectedField, obj *bug.SetTitleTimelineItem) (ret graphql.Marshaler) {
+func (ec *executionContext) _SetTitleTimelineItem_id(ctx context.Context, field graphql.CollectedField, obj *bug.SetTitleTimelineItem) (ret graphql.Marshaler) {
ctx = ec.Tracer.StartFieldExecution(ctx, field)
defer func() {
if r := recover(); r != nil {
@@ -8914,7 +8939,7 @@ func (ec *executionContext) _SetTitleTimelineItem_hash(ctx context.Context, fiel
ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx)
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
- return obj.Hash(), nil
+ return ec.resolvers.SetTitleTimelineItem().ID(rctx, obj)
})
if err != nil {
ec.Error(ctx, err)
@@ -8926,10 +8951,10 @@ func (ec *executionContext) _SetTitleTimelineItem_hash(ctx context.Context, fiel
}
return graphql.Null
}
- res := resTmp.(git.Hash)
+ res := resTmp.(string)
rctx.Result = res
ctx = ec.Tracer.StartFieldChildExecution(ctx)
- return ec.marshalNHash2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋutilᚋgitᚐHash(ctx, field.Selections, res)
+ return ec.marshalNString2string(ctx, field.Selections, res)
}
func (ec *executionContext) _SetTitleTimelineItem_author(ctx context.Context, field graphql.CollectedField, obj *bug.SetTitleTimelineItem) (ret graphql.Marshaler) {
@@ -10837,11 +10862,20 @@ func (ec *executionContext) _AddCommentOperation(ctx context.Context, sel ast.Se
switch field.Name {
case "__typename":
out.Values[i] = graphql.MarshalString("AddCommentOperation")
- case "hash":
- out.Values[i] = ec._AddCommentOperation_hash(ctx, field, obj)
- if out.Values[i] == graphql.Null {
- atomic.AddUint32(&invalids, 1)
- }
+ case "id":
+ field := field
+ out.Concurrently(i, func() (res graphql.Marshaler) {
+ defer func() {
+ if r := recover(); r != nil {
+ ec.Error(ctx, ec.Recover(ctx, r))
+ }
+ }()
+ res = ec._AddCommentOperation_id(ctx, field, obj)
+ if res == graphql.Null {
+ atomic.AddUint32(&invalids, 1)
+ }
+ return res
+ })
case "author":
out.Values[i] = ec._AddCommentOperation_author(ctx, field, obj)
if out.Values[i] == graphql.Null {
@@ -10927,11 +10961,20 @@ func (ec *executionContext) _AddCommentTimelineItem(ctx context.Context, sel ast
switch field.Name {
case "__typename":
out.Values[i] = graphql.MarshalString("AddCommentTimelineItem")
- case "hash":
- out.Values[i] = ec._AddCommentTimelineItem_hash(ctx, field, obj)
- if out.Values[i] == graphql.Null {
- atomic.AddUint32(&invalids, 1)
- }
+ case "id":
+ field := field
+ out.Concurrently(i, func() (res graphql.Marshaler) {
+ defer func() {
+ if r := recover(); r != nil {
+ ec.Error(ctx, ec.Recover(ctx, r))
+ }
+ }()
+ res = ec._AddCommentTimelineItem_id(ctx, field, obj)
+ if res == graphql.Null {
+ atomic.AddUint32(&invalids, 1)
+ }
+ return res
+ })
case "author":
out.Values[i] = ec._AddCommentTimelineItem_author(ctx, field, obj)
if out.Values[i] == graphql.Null {
@@ -11013,15 +11056,33 @@ func (ec *executionContext) _Bug(ctx context.Context, sel ast.SelectionSet, obj
case "__typename":
out.Values[i] = graphql.MarshalString("Bug")
case "id":
- out.Values[i] = ec._Bug_id(ctx, field, obj)
- if out.Values[i] == graphql.Null {
- atomic.AddUint32(&invalids, 1)
- }
+ field := field
+ out.Concurrently(i, func() (res graphql.Marshaler) {
+ defer func() {
+ if r := recover(); r != nil {
+ ec.Error(ctx, ec.Recover(ctx, r))
+ }
+ }()
+ res = ec._Bug_id(ctx, field, obj)
+ if res == graphql.Null {
+ atomic.AddUint32(&invalids, 1)
+ }
+ return res
+ })
case "humanId":
- out.Values[i] = ec._Bug_humanId(ctx, field, obj)
- if out.Values[i] == graphql.Null {
- atomic.AddUint32(&invalids, 1)
- }
+ field := field
+ out.Concurrently(i, func() (res graphql.Marshaler) {
+ defer func() {
+ if r := recover(); r != nil {
+ ec.Error(ctx, ec.Recover(ctx, r))
+ }
+ }()
+ res = ec._Bug_humanId(ctx, field, obj)
+ if res == graphql.Null {
+ atomic.AddUint32(&invalids, 1)
+ }
+ return res
+ })
case "status":
field := field
out.Concurrently(i, func() (res graphql.Marshaler) {
@@ -11583,11 +11644,20 @@ func (ec *executionContext) _CreateOperation(ctx context.Context, sel ast.Select
switch field.Name {
case "__typename":
out.Values[i] = graphql.MarshalString("CreateOperation")
- case "hash":
- out.Values[i] = ec._CreateOperation_hash(ctx, field, obj)
- if out.Values[i] == graphql.Null {
- atomic.AddUint32(&invalids, 1)
- }
+ case "id":
+ field := field
+ out.Concurrently(i, func() (res graphql.Marshaler) {
+ defer func() {
+ if r := recover(); r != nil {
+ ec.Error(ctx, ec.Recover(ctx, r))
+ }
+ }()
+ res = ec._CreateOperation_id(ctx, field, obj)
+ if res == graphql.Null {
+ atomic.AddUint32(&invalids, 1)
+ }
+ return res
+ })
case "author":
out.Values[i] = ec._CreateOperation_author(ctx, field, obj)
if out.Values[i] == graphql.Null {
@@ -11644,11 +11714,20 @@ func (ec *executionContext) _CreateTimelineItem(ctx context.Context, sel ast.Sel
switch field.Name {
case "__typename":
out.Values[i] = graphql.MarshalString("CreateTimelineItem")
- case "hash":
- out.Values[i] = ec._CreateTimelineItem_hash(ctx, field, obj)
- if out.Values[i] == graphql.Null {
- atomic.AddUint32(&invalids, 1)
- }
+ case "id":
+ field := field
+ out.Concurrently(i, func() (res graphql.Marshaler) {
+ defer func() {
+ if r := recover(); r != nil {
+ ec.Error(ctx, ec.Recover(ctx, r))
+ }
+ }()
+ res = ec._CreateTimelineItem_id(ctx, field, obj)
+ if res == graphql.Null {
+ atomic.AddUint32(&invalids, 1)
+ }
+ return res
+ })
case "author":
out.Values[i] = ec._CreateTimelineItem_author(ctx, field, obj)
if out.Values[i] == graphql.Null {
@@ -11729,11 +11808,20 @@ func (ec *executionContext) _EditCommentOperation(ctx context.Context, sel ast.S
switch field.Name {
case "__typename":
out.Values[i] = graphql.MarshalString("EditCommentOperation")
- case "hash":
- out.Values[i] = ec._EditCommentOperation_hash(ctx, field, obj)
- if out.Values[i] == graphql.Null {
- atomic.AddUint32(&invalids, 1)
- }
+ case "id":
+ field := field
+ out.Concurrently(i, func() (res graphql.Marshaler) {
+ defer func() {
+ if r := recover(); r != nil {
+ ec.Error(ctx, ec.Recover(ctx, r))
+ }
+ }()
+ res = ec._EditCommentOperation_id(ctx, field, obj)
+ if res == graphql.Null {
+ atomic.AddUint32(&invalids, 1)
+ }
+ return res
+ })
case "author":
out.Values[i] = ec._EditCommentOperation_author(ctx, field, obj)
if out.Values[i] == graphql.Null {
@@ -11754,10 +11842,19 @@ func (ec *executionContext) _EditCommentOperation(ctx context.Context, sel ast.S
return res
})
case "target":
- out.Values[i] = ec._EditCommentOperation_target(ctx, field, obj)
- if out.Values[i] == graphql.Null {
- atomic.AddUint32(&invalids, 1)
- }
+ field := field
+ out.Concurrently(i, func() (res graphql.Marshaler) {
+ defer func() {
+ if r := recover(); r != nil {
+ ec.Error(ctx, ec.Recover(ctx, r))
+ }
+ }()
+ res = ec._EditCommentOperation_target(ctx, field, obj)
+ if res == graphql.Null {
+ atomic.AddUint32(&invalids, 1)
+ }
+ return res
+ })
case "message":
out.Values[i] = ec._EditCommentOperation_message(ctx, field, obj)
if out.Values[i] == graphql.Null {
@@ -12036,11 +12133,20 @@ func (ec *executionContext) _LabelChangeOperation(ctx context.Context, sel ast.S
switch field.Name {
case "__typename":
out.Values[i] = graphql.MarshalString("LabelChangeOperation")
- case "hash":
- out.Values[i] = ec._LabelChangeOperation_hash(ctx, field, obj)
- if out.Values[i] == graphql.Null {
- atomic.AddUint32(&invalids, 1)
- }
+ case "id":
+ field := field
+ out.Concurrently(i, func() (res graphql.Marshaler) {
+ defer func() {
+ if r := recover(); r != nil {
+ ec.Error(ctx, ec.Recover(ctx, r))
+ }
+ }()
+ res = ec._LabelChangeOperation_id(ctx, field, obj)
+ if res == graphql.Null {
+ atomic.AddUint32(&invalids, 1)
+ }
+ return res
+ })
case "author":
out.Values[i] = ec._LabelChangeOperation_author(ctx, field, obj)
if out.Values[i] == graphql.Null {
@@ -12133,11 +12239,20 @@ func (ec *executionContext) _LabelChangeTimelineItem(ctx context.Context, sel as
switch field.Name {
case "__typename":
out.Values[i] = graphql.MarshalString("LabelChangeTimelineItem")
- case "hash":
- out.Values[i] = ec._LabelChangeTimelineItem_hash(ctx, field, obj)
- if out.Values[i] == graphql.Null {
- atomic.AddUint32(&invalids, 1)
- }
+ case "id":
+ field := field
+ out.Concurrently(i, func() (res graphql.Marshaler) {
+ defer func() {
+ if r := recover(); r != nil {
+ ec.Error(ctx, ec.Recover(ctx, r))
+ }
+ }()
+ res = ec._LabelChangeTimelineItem_id(ctx, field, obj)
+ if res == graphql.Null {
+ atomic.AddUint32(&invalids, 1)
+ }
+ return res
+ })
case "author":
out.Values[i] = ec._LabelChangeTimelineItem_author(ctx, field, obj)
if out.Values[i] == graphql.Null {
@@ -12588,11 +12703,20 @@ func (ec *executionContext) _SetStatusOperation(ctx context.Context, sel ast.Sel
switch field.Name {
case "__typename":
out.Values[i] = graphql.MarshalString("SetStatusOperation")
- case "hash":
- out.Values[i] = ec._SetStatusOperation_hash(ctx, field, obj)
- if out.Values[i] == graphql.Null {
- atomic.AddUint32(&invalids, 1)
- }
+ case "id":
+ field := field
+ out.Concurrently(i, func() (res graphql.Marshaler) {
+ defer func() {
+ if r := recover(); r != nil {
+ ec.Error(ctx, ec.Recover(ctx, r))
+ }
+ }()
+ res = ec._SetStatusOperation_id(ctx, field, obj)
+ if res == graphql.Null {
+ atomic.AddUint32(&invalids, 1)
+ }
+ return res
+ })
case "author":
out.Values[i] = ec._SetStatusOperation_author(ctx, field, obj)
if out.Values[i] == graphql.Null {
@@ -12648,11 +12772,20 @@ func (ec *executionContext) _SetStatusTimelineItem(ctx context.Context, sel ast.
switch field.Name {
case "__typename":
out.Values[i] = graphql.MarshalString("SetStatusTimelineItem")
- case "hash":
- out.Values[i] = ec._SetStatusTimelineItem_hash(ctx, field, obj)
- if out.Values[i] == graphql.Null {
- atomic.AddUint32(&invalids, 1)
- }
+ case "id":
+ field := field
+ out.Concurrently(i, func() (res graphql.Marshaler) {
+ defer func() {
+ if r := recover(); r != nil {
+ ec.Error(ctx, ec.Recover(ctx, r))
+ }
+ }()
+ res = ec._SetStatusTimelineItem_id(ctx, field, obj)
+ if res == graphql.Null {
+ atomic.AddUint32(&invalids, 1)
+ }
+ return res
+ })
case "author":
out.Values[i] = ec._SetStatusTimelineItem_author(ctx, field, obj)
if out.Values[i] == graphql.Null {
@@ -12708,11 +12841,20 @@ func (ec *executionContext) _SetTitleOperation(ctx context.Context, sel ast.Sele
switch field.Name {
case "__typename":
out.Values[i] = graphql.MarshalString("SetTitleOperation")
- case "hash":
- out.Values[i] = ec._SetTitleOperation_hash(ctx, field, obj)
- if out.Values[i] == graphql.Null {
- atomic.AddUint32(&invalids, 1)
- }
+ case "id":
+ field := field
+ out.Concurrently(i, func() (res graphql.Marshaler) {
+ defer func() {
+ if r := recover(); r != nil {
+ ec.Error(ctx, ec.Recover(ctx, r))
+ }
+ }()
+ res = ec._SetTitleOperation_id(ctx, field, obj)
+ if res == graphql.Null {
+ atomic.AddUint32(&invalids, 1)
+ }
+ return res
+ })
case "author":
out.Values[i] = ec._SetTitleOperation_author(ctx, field, obj)
if out.Values[i] == graphql.Null {
@@ -12798,11 +12940,20 @@ func (ec *executionContext) _SetTitleTimelineItem(ctx context.Context, sel ast.S
switch field.Name {
case "__typename":
out.Values[i] = graphql.MarshalString("SetTitleTimelineItem")
- case "hash":
- out.Values[i] = ec._SetTitleTimelineItem_hash(ctx, field, obj)
- if out.Values[i] == graphql.Null {
- atomic.AddUint32(&invalids, 1)
- }
+ case "id":
+ field := field
+ out.Concurrently(i, func() (res graphql.Marshaler) {
+ defer func() {
+ if r := recover(); r != nil {
+ ec.Error(ctx, ec.Recover(ctx, r))
+ }
+ }()
+ res = ec._SetTitleTimelineItem_id(ctx, field, obj)
+ if res == graphql.Null {
+ atomic.AddUint32(&invalids, 1)
+ }
+ return res
+ })
case "author":
out.Values[i] = ec._SetTitleTimelineItem_author(ctx, field, obj)
if out.Values[i] == graphql.Null {
diff --git a/graphql/resolvers/bug.go b/graphql/resolvers/bug.go
index 37766b95..8f994c0b 100644
--- a/graphql/resolvers/bug.go
+++ b/graphql/resolvers/bug.go
@@ -15,6 +15,14 @@ var _ graph.BugResolver = &bugResolver{}
type bugResolver struct{}
+func (bugResolver) ID(ctx context.Context, obj *bug.Snapshot) (string, error) {
+ return obj.Id().String(), nil
+}
+
+func (bugResolver) HumanID(ctx context.Context, obj *bug.Snapshot) (string, error) {
+ return obj.Id().Human(), nil
+}
+
func (bugResolver) Status(ctx context.Context, obj *bug.Snapshot) (models.Status, error) {
return convertStatus(obj.Status)
}
diff --git a/graphql/resolvers/identity.go b/graphql/resolvers/identity.go
index 05f7207e..ee40d4d8 100644
--- a/graphql/resolvers/identity.go
+++ b/graphql/resolvers/identity.go
@@ -12,11 +12,11 @@ var _ graph.IdentityResolver = &identityResolver{}
type identityResolver struct{}
func (identityResolver) ID(ctx context.Context, obj *identity.Interface) (string, error) {
- return (*obj).Id(), nil
+ return (*obj).Id().String(), nil
}
func (identityResolver) HumanID(ctx context.Context, obj *identity.Interface) (string, error) {
- return (*obj).HumanId(), nil
+ return (*obj).Id().Human(), nil
}
func (identityResolver) Name(ctx context.Context, obj *identity.Interface) (*string, error) {
diff --git a/graphql/resolvers/operations.go b/graphql/resolvers/operations.go
index 19b2b17f..ec803c1f 100644
--- a/graphql/resolvers/operations.go
+++ b/graphql/resolvers/operations.go
@@ -6,39 +6,74 @@ import (
"time"
"github.com/MichaelMure/git-bug/bug"
+ "github.com/MichaelMure/git-bug/graphql/graph"
"github.com/MichaelMure/git-bug/graphql/models"
)
+var _ graph.CreateOperationResolver = createOperationResolver{}
+
type createOperationResolver struct{}
+func (createOperationResolver) ID(ctx context.Context, obj *bug.CreateOperation) (string, error) {
+ return obj.Id().String(), nil
+}
+
func (createOperationResolver) Date(ctx context.Context, obj *bug.CreateOperation) (*time.Time, error) {
t := obj.Time()
return &t, nil
}
+var _ graph.AddCommentOperationResolver = addCommentOperationResolver{}
+
type addCommentOperationResolver struct{}
+func (addCommentOperationResolver) ID(ctx context.Context, obj *bug.AddCommentOperation) (string, error) {
+ return obj.Id().String(), nil
+}
+
func (addCommentOperationResolver) Date(ctx context.Context, obj *bug.AddCommentOperation) (*time.Time, error) {
t := obj.Time()
return &t, nil
}
+var _ graph.EditCommentOperationResolver = editCommentOperationResolver{}
+
type editCommentOperationResolver struct{}
+func (editCommentOperationResolver) ID(ctx context.Context, obj *bug.EditCommentOperation) (string, error) {
+ return obj.Id().String(), nil
+}
+
+func (editCommentOperationResolver) Target(ctx context.Context, obj *bug.EditCommentOperation) (string, error) {
+ return obj.Target.String(), nil
+}
+
func (editCommentOperationResolver) Date(ctx context.Context, obj *bug.EditCommentOperation) (*time.Time, error) {
t := obj.Time()
return &t, nil
}
-type labelChangeOperation struct{}
+var _ graph.LabelChangeOperationResolver = labelChangeOperationResolver{}
+
+type labelChangeOperationResolver struct{}
-func (labelChangeOperation) Date(ctx context.Context, obj *bug.LabelChangeOperation) (*time.Time, error) {
+func (labelChangeOperationResolver) ID(ctx context.Context, obj *bug.LabelChangeOperation) (string, error) {
+ return obj.Id().String(), nil
+}
+
+func (labelChangeOperationResolver) Date(ctx context.Context, obj *bug.LabelChangeOperation) (*time.Time, error) {
t := obj.Time()
return &t, nil
}
+var _ graph.SetStatusOperationResolver = setStatusOperationResolver{}
+
type setStatusOperationResolver struct{}
+func (setStatusOperationResolver) ID(ctx context.Context, obj *bug.SetStatusOperation) (string, error) {
+ return obj.Id().String(), nil
+}
+
func (setStatusOperationResolver) Date(ctx context.Context, obj *bug.SetStatusOperation) (*time.Time, error) {
t := obj.Time()
return &t, nil
@@ -48,8 +83,14 @@ func (setStatusOperationResolver) Status(ctx context.Context, obj *bug.SetStatus
return convertStatus(obj.Status)
}
+var _ graph.SetTitleOperationResolver = setTitleOperationResolver{}
+
type setTitleOperationResolver struct{}
+func (setTitleOperationResolver) ID(ctx context.Context, obj *bug.SetTitleOperation) (string, error) {
+ return obj.Id().String(), nil
+}
+
func (setTitleOperationResolver) Date(ctx context.Context, obj *bug.SetTitleOperation) (*time.Time, error) {
t := obj.Time()
return &t, nil
diff --git a/graphql/resolvers/repo.go b/graphql/resolvers/repo.go
index 68a3ce0a..ac9c162f 100644
--- a/graphql/resolvers/repo.go
+++ b/graphql/resolvers/repo.go
@@ -5,6 +5,7 @@ import (
"github.com/MichaelMure/git-bug/bug"
"github.com/MichaelMure/git-bug/cache"
+ "github.com/MichaelMure/git-bug/entity"
"github.com/MichaelMure/git-bug/graphql/connections"
"github.com/MichaelMure/git-bug/graphql/graph"
"github.com/MichaelMure/git-bug/graphql/models"
@@ -38,7 +39,7 @@ func (repoResolver) AllBugs(ctx context.Context, obj *models.Repository, after *
source := obj.Repo.QueryBugs(query)
// The edger create a custom edge holding just the id
- edger := func(id string, offset int) connections.Edge {
+ edger := func(id entity.Id, offset int) connections.Edge {
return connections.LazyBugEdge{
Id: id,
Cursor: connections.OffsetToCursor(offset),
@@ -46,7 +47,7 @@ func (repoResolver) AllBugs(ctx context.Context, obj *models.Repository, after *
}
// The conMaker will finally load and compile bugs from git to replace the selected edges
- conMaker := func(lazyBugEdges []*connections.LazyBugEdge, lazyNode []string, info *models.PageInfo, totalCount int) (*models.BugConnection, error) {
+ conMaker := func(lazyBugEdges []*connections.LazyBugEdge, lazyNode []entity.Id, info *models.PageInfo, totalCount int) (*models.BugConnection, error) {
edges := make([]*models.BugEdge, len(lazyBugEdges))
nodes := make([]*bug.Snapshot, len(lazyBugEdges))
@@ -99,7 +100,7 @@ func (repoResolver) AllIdentities(ctx context.Context, obj *models.Repository, a
source := obj.Repo.AllIdentityIds()
// The edger create a custom edge holding just the id
- edger := func(id string, offset int) connections.Edge {
+ edger := func(id entity.Id, offset int) connections.Edge {
return connections.LazyIdentityEdge{
Id: id,
Cursor: connections.OffsetToCursor(offset),
@@ -107,7 +108,7 @@ func (repoResolver) AllIdentities(ctx context.Context, obj *models.Repository, a
}
// The conMaker will finally load and compile identities from git to replace the selected edges
- conMaker := func(lazyIdentityEdges []*connections.LazyIdentityEdge, lazyNode []string, info *models.PageInfo, totalCount int) (*models.IdentityConnection, error) {
+ conMaker := func(lazyIdentityEdges []*connections.LazyIdentityEdge, lazyNode []entity.Id, info *models.PageInfo, totalCount int) (*models.IdentityConnection, error) {
edges := make([]*models.IdentityEdge, len(lazyIdentityEdges))
nodes := make([]identity.Interface, len(lazyIdentityEdges))
diff --git a/graphql/resolvers/root.go b/graphql/resolvers/root.go
index 8873b723..3b129f3b 100644
--- a/graphql/resolvers/root.go
+++ b/graphql/resolvers/root.go
@@ -87,7 +87,7 @@ func (r RootResolver) EditCommentOperation() graph.EditCommentOperationResolver
}
func (RootResolver) LabelChangeOperation() graph.LabelChangeOperationResolver {
- return &labelChangeOperation{}
+ return &labelChangeOperationResolver{}
}
func (RootResolver) SetStatusOperation() graph.SetStatusOperationResolver {
diff --git a/graphql/resolvers/timeline.go b/graphql/resolvers/timeline.go
index 27f799ba..b206f898 100644
--- a/graphql/resolvers/timeline.go
+++ b/graphql/resolvers/timeline.go
@@ -5,9 +5,12 @@ import (
"time"
"github.com/MichaelMure/git-bug/bug"
+ "github.com/MichaelMure/git-bug/graphql/graph"
"github.com/MichaelMure/git-bug/graphql/models"
)
+var _ graph.CommentHistoryStepResolver = commentHistoryStepResolver{}
+
type commentHistoryStepResolver struct{}
func (commentHistoryStepResolver) Date(ctx context.Context, obj *bug.CommentHistoryStep) (*time.Time, error) {
@@ -15,8 +18,14 @@ func (commentHistoryStepResolver) Date(ctx context.Context, obj *bug.CommentHist
return &t, nil
}
+var _ graph.AddCommentTimelineItemResolver = addCommentTimelineItemResolver{}
+
type addCommentTimelineItemResolver struct{}
+func (addCommentTimelineItemResolver) ID(ctx context.Context, obj *bug.AddCommentTimelineItem) (string, error) {
+ return obj.Id().String(), nil
+}
+
func (addCommentTimelineItemResolver) CreatedAt(ctx context.Context, obj *bug.AddCommentTimelineItem) (*time.Time, error) {
t := obj.CreatedAt.Time()
return &t, nil
@@ -27,8 +36,14 @@ func (addCommentTimelineItemResolver) LastEdit(ctx context.Context, obj *bug.Add
return &t, nil
}
+var _ graph.CreateTimelineItemResolver = createTimelineItemResolver{}
+
type createTimelineItemResolver struct{}
+func (createTimelineItemResolver) ID(ctx context.Context, obj *bug.CreateTimelineItem) (string, error) {
+ return obj.Id().String(), nil
+}
+
func (createTimelineItemResolver) CreatedAt(ctx context.Context, obj *bug.CreateTimelineItem) (*time.Time, error) {
t := obj.CreatedAt.Time()
return &t, nil
@@ -39,15 +54,27 @@ func (createTimelineItemResolver) LastEdit(ctx context.Context, obj *bug.CreateT
return &t, nil
}
+var _ graph.LabelChangeTimelineItemResolver = labelChangeTimelineItem{}
+
type labelChangeTimelineItem struct{}
+func (labelChangeTimelineItem) ID(ctx context.Context, obj *bug.LabelChangeTimelineItem) (string, error) {
+ return obj.Id().String(), nil
+}
+
func (labelChangeTimelineItem) Date(ctx context.Context, obj *bug.LabelChangeTimelineItem) (*time.Time, error) {
t := obj.UnixTime.Time()
return &t, nil
}
+var _ graph.SetStatusTimelineItemResolver = setStatusTimelineItem{}
+
type setStatusTimelineItem struct{}
+func (setStatusTimelineItem) ID(ctx context.Context, obj *bug.SetStatusTimelineItem) (string, error) {
+ return obj.Id().String(), nil
+}
+
func (setStatusTimelineItem) Date(ctx context.Context, obj *bug.SetStatusTimelineItem) (*time.Time, error) {
t := obj.UnixTime.Time()
return &t, nil
@@ -57,8 +84,14 @@ func (setStatusTimelineItem) Status(ctx context.Context, obj *bug.SetStatusTimel
return convertStatus(obj.Status)
}
+var _ graph.SetTitleTimelineItemResolver = setTitleTimelineItem{}
+
type setTitleTimelineItem struct{}
+func (setTitleTimelineItem) ID(ctx context.Context, obj *bug.SetTitleTimelineItem) (string, error) {
+ return obj.Id().String(), nil
+}
+
func (setTitleTimelineItem) Date(ctx context.Context, obj *bug.SetTitleTimelineItem) (*time.Time, error) {
t := obj.UnixTime.Time()
return &t, nil
diff --git a/graphql/schema/operations.graphql b/graphql/schema/operations.graphql
index d37df2e2..18e0929c 100644
--- a/graphql/schema/operations.graphql
+++ b/graphql/schema/operations.graphql
@@ -1,7 +1,7 @@
"""An operation applied to a bug."""
interface Operation {
- """The hash of the operation"""
- hash: Hash!
+ """The identifier of the operation"""
+ id: String!
"""The operations author."""
author: Identity!
"""The datetime when this operation was issued."""
@@ -27,8 +27,8 @@ type OperationEdge {
# Operations
type CreateOperation implements Operation & Authored {
- """The hash of the operation"""
- hash: Hash!
+ """The identifier of the operation"""
+ id: String!
"""The author of this object."""
author: Identity!
"""The datetime when this operation was issued."""
@@ -40,8 +40,8 @@ type CreateOperation implements Operation & Authored {
}
type SetTitleOperation implements Operation & Authored {
- """The hash of the operation"""
- hash: Hash!
+ """The identifier of the operation"""
+ id: String!
"""The author of this object."""
author: Identity!
"""The datetime when this operation was issued."""
@@ -52,8 +52,8 @@ type SetTitleOperation implements Operation & Authored {
}
type AddCommentOperation implements Operation & Authored {
- """The hash of the operation"""
- hash: Hash!
+ """The identifier of the operation"""
+ id: String!
"""The author of this object."""
author: Identity!
"""The datetime when this operation was issued."""
@@ -64,21 +64,21 @@ type AddCommentOperation implements Operation & Authored {
}
type EditCommentOperation implements Operation & Authored {
- """The hash of the operation"""
- hash: Hash!
+ """The identifier of the operation"""
+ id: String!
"""The author of this object."""
author: Identity!
"""The datetime when this operation was issued."""
date: Time!
- target: Hash!
+ target: String!
message: String!
files: [Hash!]!
}
type SetStatusOperation implements Operation & Authored {
- """The hash of the operation"""
- hash: Hash!
+ """The identifier of the operation"""
+ id: String!
"""The author of this object."""
author: Identity!
"""The datetime when this operation was issued."""
@@ -88,8 +88,8 @@ type SetStatusOperation implements Operation & Authored {
}
type LabelChangeOperation implements Operation & Authored {
- """The hash of the operation"""
- hash: Hash!
+ """The identifier of the operation"""
+ id: String!
"""The author of this object."""
author: Identity!
"""The datetime when this operation was issued."""
diff --git a/graphql/schema/timeline.graphql b/graphql/schema/timeline.graphql
index ccc89b87..12462aa3 100644
--- a/graphql/schema/timeline.graphql
+++ b/graphql/schema/timeline.graphql
@@ -1,7 +1,7 @@
"""An item in the timeline of events"""
interface TimelineItem {
- """The hash of the source operation"""
- hash: Hash!
+ """The identifier of the source operation"""
+ id: String!
}
"""CommentHistoryStep hold one version of a message in the history"""
@@ -30,8 +30,8 @@ type TimelineItemEdge {
"""CreateTimelineItem is a TimelineItem that represent the creation of a bug and its message edition history"""
type CreateTimelineItem implements TimelineItem & Authored {
- """The hash of the source operation"""
- hash: Hash!
+ """The identifier of the source operation"""
+ id: String!
author: Identity!
message: String!
messageIsEmpty: Boolean!
@@ -44,8 +44,8 @@ type CreateTimelineItem implements TimelineItem & Authored {
"""AddCommentTimelineItem is a TimelineItem that represent a Comment and its edition history"""
type AddCommentTimelineItem implements TimelineItem & Authored {
- """The hash of the source operation"""
- hash: Hash!
+ """The identifier of the source operation"""
+ id: String!
author: Identity!
message: String!
messageIsEmpty: Boolean!
@@ -58,8 +58,8 @@ type AddCommentTimelineItem implements TimelineItem & Authored {
"""LabelChangeTimelineItem is a TimelineItem that represent a change in the labels of a bug"""
type LabelChangeTimelineItem implements TimelineItem & Authored {
- """The hash of the source operation"""
- hash: Hash!
+ """The identifier of the source operation"""
+ id: String!
author: Identity!
date: Time!
added: [Label!]!
@@ -68,8 +68,8 @@ type LabelChangeTimelineItem implements TimelineItem & Authored {
"""SetStatusTimelineItem is a TimelineItem that represent a change in the status of a bug"""
type SetStatusTimelineItem implements TimelineItem & Authored {
- """The hash of the source operation"""
- hash: Hash!
+ """The identifier of the source operation"""
+ id: String!
author: Identity!
date: Time!
status: Status!
@@ -77,8 +77,8 @@ type SetStatusTimelineItem implements TimelineItem & Authored {
"""LabelChangeTimelineItem is a TimelineItem that represent a change in the title of a bug"""
type SetTitleTimelineItem implements TimelineItem & Authored {
- """The hash of the source operation"""
- hash: Hash!
+ """The identifier of the source operation"""
+ id: String!
author: Identity!
date: Time!
title: String!
diff --git a/identity/bare.go b/identity/bare.go
index 6af794dd..a243f074 100644
--- a/identity/bare.go
+++ b/identity/bare.go
@@ -6,6 +6,7 @@ import (
"fmt"
"strings"
+ "github.com/MichaelMure/git-bug/entity"
"github.com/MichaelMure/git-bug/repository"
"github.com/MichaelMure/git-bug/util/lamport"
"github.com/MichaelMure/git-bug/util/text"
@@ -13,6 +14,7 @@ import (
)
var _ Interface = &Bare{}
+var _ entity.Interface = &Bare{}
// Bare is a very minimal identity, designed to be fully embedded directly along
// other data.
@@ -20,7 +22,7 @@ var _ Interface = &Bare{}
// in particular, this identity is designed to be compatible with the handling of
// identities in the early version of git-bug.
type Bare struct {
- id string
+ id entity.Id
name string
email string
login string
@@ -28,11 +30,16 @@ type Bare struct {
}
func NewBare(name string, email string) *Bare {
- return &Bare{name: name, email: email}
+ return &Bare{id: entity.UnsetId, name: name, email: email}
}
func NewBareFull(name string, email string, login string, avatarUrl string) *Bare {
- return &Bare{name: name, email: email, login: login, avatarUrl: avatarUrl}
+ return &Bare{id: entity.UnsetId, name: name, email: email, login: login, avatarUrl: avatarUrl}
+}
+
+func deriveId(data []byte) entity.Id {
+ sum := sha256.Sum256(data)
+ return entity.Id(fmt.Sprintf("%x", sum))
}
type bareIdentityJSON struct {
@@ -52,6 +59,9 @@ func (i *Bare) MarshalJSON() ([]byte, error) {
}
func (i *Bare) UnmarshalJSON(data []byte) error {
+ // Compute the Id when loading the op from disk.
+ i.id = deriveId(data)
+
aux := bareIdentityJSON{}
if err := json.Unmarshal(data, &aux); err != nil {
@@ -67,30 +77,28 @@ func (i *Bare) UnmarshalJSON(data []byte) error {
}
// Id return the Identity identifier
-func (i *Bare) Id() string {
- // We don't have a proper ID at hand, so let's hash all the data to get one.
- // Hopefully the
-
- if i.id != "" {
- return i.id
- }
+func (i *Bare) Id() entity.Id {
+ // We don't have a proper Id at hand, so let's hash all the data to get one.
- data, err := json.Marshal(i)
- if err != nil {
- panic(err)
+ if i.id == "" {
+ // something went really wrong
+ panic("identity's id not set")
}
+ if i.id == entity.UnsetId {
+ // This means we are trying to get the identity identifier *before* it has been stored
+ // 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.
- h := fmt.Sprintf("%x", sha256.New().Sum(data)[:16])
- i.id = string(h)
+ data, err := json.Marshal(i)
+ if err != nil {
+ panic(err)
+ }
+ i.id = deriveId(data)
+ }
return i.id
}
-// HumanId return the Identity identifier truncated for human consumption
-func (i *Bare) HumanId() string {
- return FormatHumanID(i.Id())
-}
-
// Name return the last version of the name
func (i *Bare) Name() string {
return i.name
diff --git a/identity/bare_test.go b/identity/bare_test.go
index 7db9f644..335c8d37 100644
--- a/identity/bare_test.go
+++ b/identity/bare_test.go
@@ -5,12 +5,15 @@ import (
"testing"
"github.com/stretchr/testify/assert"
+
+ "github.com/MichaelMure/git-bug/entity"
)
func TestBare_Id(t *testing.T) {
i := NewBare("name", "email")
id := i.Id()
- assert.Equal(t, "7b226e616d65223a226e616d65222c22", id)
+ expected := entity.Id("e18b853fbd89d5d40ca24811539c9a800c705abd9232f396954e8ca8bb63fa8a")
+ assert.Equal(t, expected, id)
}
func TestBareSerialize(t *testing.T) {
@@ -28,5 +31,7 @@ func TestBareSerialize(t *testing.T) {
err = json.Unmarshal(data, &after)
assert.NoError(t, err)
+ before.id = after.id
+
assert.Equal(t, before, &after)
}
diff --git a/identity/common.go b/identity/common.go
index 2f2b9042..007e10d6 100644
--- a/identity/common.go
+++ b/identity/common.go
@@ -4,17 +4,14 @@ import (
"encoding/json"
"errors"
"fmt"
- "strings"
+
+ "github.com/MichaelMure/git-bug/entity"
)
var ErrIdentityNotExist = errors.New("identity doesn't exist")
-type ErrMultipleMatch struct {
- Matching []string
-}
-
-func (e ErrMultipleMatch) Error() string {
- return fmt.Sprintf("Multiple matching identities found:\n%s", strings.Join(e.Matching, "\n"))
+func NewErrMultipleMatch(matching []entity.Id) *entity.ErrMultipleMatch {
+ return entity.NewErrMultipleMatch("identity", matching)
}
// Custom unmarshaling function to allow package user to delegate
@@ -37,11 +34,11 @@ func UnmarshalJSON(raw json.RawMessage) (Interface, error) {
}
// Fallback on a legacy Bare identity
- var b Bare
+ b := &Bare{}
- err = json.Unmarshal(raw, &b)
+ err = json.Unmarshal(raw, b)
if err == nil && (b.name != "" || b.login != "") {
- return &b, nil
+ return b, nil
}
// abort if we have an error other than the wrong type
diff --git a/identity/identity.go b/identity/identity.go
index f952bbf9..765b77cd 100644
--- a/identity/identity.go
+++ b/identity/identity.go
@@ -10,6 +10,7 @@ import (
"github.com/pkg/errors"
+ "github.com/MichaelMure/git-bug/entity"
"github.com/MichaelMure/git-bug/repository"
"github.com/MichaelMure/git-bug/util/git"
"github.com/MichaelMure/git-bug/util/lamport"
@@ -21,18 +22,16 @@ const identityRemoteRefPattern = "refs/remotes/%s/identities/"
const versionEntryName = "version"
const identityConfigKey = "git-bug.identity"
-const idLength = 40
-const humanIdLength = 7
-
var ErrNonFastForwardMerge = errors.New("non fast-forward identity merge")
var ErrNoIdentitySet = errors.New("to interact with bugs, an identity first needs to be created using \"git bug user create\" or \"git bug user adopt\"")
var ErrMultipleIdentitiesSet = errors.New("multiple user identities set")
var _ Interface = &Identity{}
+var _ entity.Interface = &Identity{}
type Identity struct {
// Id used as unique identifier
- id string
+ id entity.Id
// all the successive version of the identity
versions []*Version
@@ -43,6 +42,7 @@ type Identity struct {
func NewIdentity(name string, email string) *Identity {
return &Identity{
+ id: entity.UnsetId,
versions: []*Version{
{
name: name,
@@ -55,6 +55,7 @@ func NewIdentity(name string, email string) *Identity {
func NewIdentityFull(name string, email string, login string, avatarUrl string) *Identity {
return &Identity{
+ id: entity.UnsetId,
versions: []*Version{
{
name: name,
@@ -70,7 +71,7 @@ func NewIdentityFull(name string, email string, login string, avatarUrl string)
// MarshalJSON will only serialize the id
func (i *Identity) MarshalJSON() ([]byte, error) {
return json.Marshal(&IdentityStub{
- id: i.Id(),
+ id: i.id,
})
}
@@ -82,7 +83,7 @@ func (i *Identity) UnmarshalJSON(data []byte) error {
}
// ReadLocal load a local Identity from the identities data available in git
-func ReadLocal(repo repository.Repo, id string) (*Identity, error) {
+func ReadLocal(repo repository.Repo, id entity.Id) (*Identity, error) {
ref := fmt.Sprintf("%s%s", identityRefPattern, id)
return read(repo, ref)
}
@@ -96,10 +97,10 @@ func ReadRemote(repo repository.Repo, remote string, id string) (*Identity, erro
// read will load and parse an identity from git
func read(repo repository.Repo, ref string) (*Identity, 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)
@@ -233,7 +234,7 @@ func IsUserIdentitySet(repo repository.RepoCommon) (bool, error) {
// SetUserIdentity store the user identity's id in the git config
func SetUserIdentity(repo repository.RepoCommon, identity *Identity) error {
- return repo.StoreConfig(identityConfigKey, identity.Id())
+ return repo.StoreConfig(identityConfigKey, identity.Id().String())
}
// GetUserIdentity read the current user identity, set with a git config entry
@@ -251,9 +252,13 @@ func GetUserIdentity(repo repository.Repo) (*Identity, error) {
return nil, ErrMultipleIdentitiesSet
}
- var id string
+ var id entity.Id
for _, val := range configs {
- id = val
+ id = entity.Id(val)
+ }
+
+ if err := id.Validate(); err != nil {
+ return nil, err
}
i, err := ReadLocal(repo, id)
@@ -326,8 +331,8 @@ func (i *Identity) Commit(repo repository.ClockedRepo) error {
v.commitHash = commitHash
// if it was the first commit, use the commit hash as the Identity id
- if i.id == "" {
- i.id = string(commitHash)
+ if i.id == "" || i.id == entity.UnsetId {
+ i.id = entity.Id(commitHash)
}
}
@@ -410,7 +415,7 @@ func (i *Identity) Merge(repo repository.Repo, other *Identity) (bool, error) {
}
if modified {
- err := repo.UpdateRef(identityRefPattern+i.id, i.lastCommit)
+ err := repo.UpdateRef(identityRefPattern+i.id.String(), i.lastCommit)
if err != nil {
return false, err
}
@@ -439,8 +444,8 @@ func (i *Identity) Validate() error {
lastTime = v.time
}
- // The identity ID should be the hash of the first commit
- if i.versions[0].commitHash != "" && string(i.versions[0].commitHash) != i.id {
+ // The identity Id should be the hash of the first commit
+ if i.versions[0].commitHash != "" && string(i.versions[0].commitHash) != i.id.String() {
return fmt.Errorf("identity id should be the first commit hash")
}
@@ -456,7 +461,7 @@ func (i *Identity) lastVersion() *Version {
}
// Id return the Identity identifier
-func (i *Identity) Id() string {
+func (i *Identity) Id() entity.Id {
if i.id == "" {
// simply panic as it would be a coding error
// (using an id of an identity not stored yet)
@@ -465,16 +470,6 @@ func (i *Identity) Id() string {
return i.id
}
-// HumanId return the Identity identifier truncated for human consumption
-func (i *Identity) HumanId() string {
- return FormatHumanID(i.Id())
-}
-
-func FormatHumanID(id string) string {
- format := fmt.Sprintf("%%.%ds", humanIdLength)
- return fmt.Sprintf(format, id)
-}
-
// Name return the last version of the name
func (i *Identity) Name() string {
return i.lastVersion().name
diff --git a/identity/identity_actions.go b/identity/identity_actions.go
index 38d83b19..aa6a2a91 100644
--- a/identity/identity_actions.go
+++ b/identity/identity_actions.go
@@ -59,8 +59,13 @@ func MergeAll(repo repository.ClockedRepo, remote string) <-chan entity.MergeRes
}
for _, remoteRef := range remoteRefs {
- refSplitted := strings.Split(remoteRef, "/")
- id := refSplitted[len(refSplitted)-1]
+ refSplit := strings.Split(remoteRef, "/")
+ id := entity.Id(refSplit[len(refSplit)-1])
+
+ if err := id.Validate(); err != nil {
+ out <- entity.NewMergeInvalidStatus(id, errors.Wrap(err, "invalid ref").Error())
+ continue
+ }
remoteIdentity, err := read(repo, remoteRef)
@@ -75,7 +80,7 @@ func MergeAll(repo repository.ClockedRepo, remote string) <-chan entity.MergeRes
continue
}
- localRef := identityRefPattern + remoteIdentity.Id()
+ localRef := identityRefPattern + remoteIdentity.Id().String()
localExist, err := repo.RefExist(localRef)
if err != nil {
diff --git a/identity/identity_stub.go b/identity/identity_stub.go
index a2cee0ad..be52ffc0 100644
--- a/identity/identity_stub.go
+++ b/identity/identity_stub.go
@@ -3,6 +3,7 @@ package identity
import (
"encoding/json"
+ "github.com/MichaelMure/git-bug/entity"
"github.com/MichaelMure/git-bug/repository"
"github.com/MichaelMure/git-bug/util/lamport"
"github.com/MichaelMure/git-bug/util/timestamp"
@@ -16,13 +17,13 @@ var _ Interface = &IdentityStub{}
// When this JSON is deserialized, an IdentityStub is returned instead, to be replaced
// later by the proper Identity, loaded from the Repo.
type IdentityStub struct {
- id string
+ id entity.Id
}
func (i *IdentityStub) MarshalJSON() ([]byte, error) {
// TODO: add a type marker
return json.Marshal(struct {
- Id string `json:"id"`
+ Id entity.Id `json:"id"`
}{
Id: i.id,
})
@@ -30,7 +31,7 @@ func (i *IdentityStub) MarshalJSON() ([]byte, error) {
func (i *IdentityStub) UnmarshalJSON(data []byte) error {
aux := struct {
- Id string `json:"id"`
+ Id entity.Id `json:"id"`
}{}
if err := json.Unmarshal(data, &aux); err != nil {
@@ -43,15 +44,10 @@ func (i *IdentityStub) UnmarshalJSON(data []byte) error {
}
// Id return the Identity identifier
-func (i *IdentityStub) Id() string {
+func (i *IdentityStub) Id() entity.Id {
return i.id
}
-// HumanId return the Identity identifier truncated for human consumption
-func (i *IdentityStub) HumanId() string {
- return FormatHumanID(i.Id())
-}
-
func (IdentityStub) Name() string {
panic("identities needs to be properly loaded with identity.ReadLocal()")
}
diff --git a/identity/identity_stub_test.go b/identity/identity_stub_test.go
index 3d94cd3e..b01a718c 100644
--- a/identity/identity_stub_test.go
+++ b/identity/identity_stub_test.go
@@ -19,5 +19,8 @@ func TestIdentityStubSerialize(t *testing.T) {
err = json.Unmarshal(data, &after)
assert.NoError(t, err)
+ // enforce creating the Id
+ before.Id()
+
assert.Equal(t, before, &after)
}
diff --git a/identity/identity_test.go b/identity/identity_test.go
index 2ddb64ea..f91c548f 100644
--- a/identity/identity_test.go
+++ b/identity/identity_test.go
@@ -4,6 +4,7 @@ import (
"encoding/json"
"testing"
+ "github.com/MichaelMure/git-bug/entity"
"github.com/MichaelMure/git-bug/repository"
"github.com/stretchr/testify/assert"
)
@@ -15,6 +16,7 @@ func TestIdentityCommitLoad(t *testing.T) {
// single version
identity := &Identity{
+ id: entity.UnsetId,
versions: []*Version{
{
name: "René Descartes",
@@ -36,6 +38,7 @@ func TestIdentityCommitLoad(t *testing.T) {
// multiple version
identity = &Identity{
+ id: entity.UnsetId,
versions: []*Version{
{
time: 100,
@@ -114,6 +117,7 @@ func commitsAreSet(t *testing.T, identity *Identity) {
// Test that the correct crypto keys are returned for a given lamport time
func TestIdentity_ValidKeysAtTime(t *testing.T) {
identity := Identity{
+ id: entity.UnsetId,
versions: []*Version{
{
time: 100,
@@ -215,6 +219,7 @@ func TestJSON(t *testing.T) {
mockRepo := repository.NewMockRepoForTest()
identity := &Identity{
+ id: entity.UnsetId,
versions: []*Version{
{
name: "René Descartes",
@@ -223,7 +228,7 @@ func TestJSON(t *testing.T) {
},
}
- // commit to make sure we have an ID
+ // commit to make sure we have an Id
err := identity.Commit(mockRepo)
assert.Nil(t, err)
assert.NotEmpty(t, identity.id)
diff --git a/identity/interface.go b/identity/interface.go
index 88f1d9a7..54a9da78 100644
--- a/identity/interface.go
+++ b/identity/interface.go
@@ -1,6 +1,7 @@
package identity
import (
+ "github.com/MichaelMure/git-bug/entity"
"github.com/MichaelMure/git-bug/repository"
"github.com/MichaelMure/git-bug/util/lamport"
"github.com/MichaelMure/git-bug/util/timestamp"
@@ -8,10 +9,7 @@ import (
type Interface interface {
// Id return the Identity identifier
- Id() string
-
- // HumanId return the Identity identifier truncated for human consumption
- HumanId() string
+ Id() entity.Id
// Name return the last version of the name
Name() string
diff --git a/identity/resolver.go b/identity/resolver.go
index 7facfc0c..70fac74c 100644
--- a/identity/resolver.go
+++ b/identity/resolver.go
@@ -1,11 +1,14 @@
package identity
-import "github.com/MichaelMure/git-bug/repository"
+import (
+ "github.com/MichaelMure/git-bug/entity"
+ "github.com/MichaelMure/git-bug/repository"
+)
// Resolver define the interface of an Identity resolver, able to load
// an identity from, for example, a repo or a cache.
type Resolver interface {
- ResolveIdentity(id string) (Interface, error)
+ ResolveIdentity(id entity.Id) (Interface, error)
}
// DefaultResolver is a Resolver loading Identities directly from a Repo
@@ -17,6 +20,6 @@ func NewSimpleResolver(repo repository.Repo) *SimpleResolver {
return &SimpleResolver{repo: repo}
}
-func (r *SimpleResolver) ResolveIdentity(id string) (Interface, error) {
+func (r *SimpleResolver) ResolveIdentity(id entity.Id) (Interface, error) {
return ReadLocal(r.repo, id)
}
diff --git a/termui/bug_table.go b/termui/bug_table.go
index 4293a3c2..8d69d665 100644
--- a/termui/bug_table.go
+++ b/termui/bug_table.go
@@ -25,7 +25,7 @@ type bugTable struct {
repo *cache.RepoCache
queryStr string
query *cache.Query
- allIds []string
+ allIds []entity.Id
excerpts []*cache.BugExcerpt
pageCursor int
selectCursor int
@@ -308,7 +308,7 @@ func (bt *bugTable) render(v *gocui.View, maxX int) {
lastEditTime := time.Unix(excerpt.EditUnixTime, 0)
- id := text.LeftPadMaxLine(excerpt.HumanId(), columnWidths["id"], 1)
+ id := text.LeftPadMaxLine(excerpt.Id.Human(), columnWidths["id"], 1)
status := text.LeftPadMaxLine(excerpt.Status.String(), columnWidths["status"], 1)
title := text.LeftPadMaxLine(excerpt.Title, columnWidths["title"], 1)
author := text.LeftPadMaxLine(authorDisplayName, columnWidths["author"], 1)
@@ -482,7 +482,7 @@ func (bt *bugTable) pull(g *gocui.Gui, v *gocui.View) error {
})
} else {
_, _ = fmt.Fprintf(&buffer, "%s%s: %s",
- beginLine, colors.Cyan(result.Entity.HumanId()), result,
+ beginLine, colors.Cyan(result.Entity.Id().Human()), result,
)
beginLine = "\n"
diff --git a/termui/show_bug.go b/termui/show_bug.go
index 68a46a31..228b85b0 100644
--- a/termui/show_bug.go
+++ b/termui/show_bug.go
@@ -7,8 +7,8 @@ import (
"github.com/MichaelMure/git-bug/bug"
"github.com/MichaelMure/git-bug/cache"
+ "github.com/MichaelMure/git-bug/entity"
"github.com/MichaelMure/git-bug/util/colors"
- "github.com/MichaelMure/git-bug/util/git"
"github.com/MichaelMure/git-bug/util/text"
"github.com/MichaelMure/gocui"
)
@@ -214,7 +214,7 @@ func (sb *showBug) renderMain(g *gocui.Gui, mainView *gocui.View) error {
}
bugHeader := fmt.Sprintf("[%s] %s\n\n[%s] %s opened this bug on %s%s",
- colors.Cyan(snap.HumanId()),
+ colors.Cyan(snap.Id().Human()),
colors.Bold(snap.Title),
colors.Yellow(snap.Status),
colors.Magenta(snap.Author.DisplayName()),
@@ -232,7 +232,7 @@ func (sb *showBug) renderMain(g *gocui.Gui, mainView *gocui.View) error {
y0 += lines + 1
for _, op := range snap.Timeline {
- viewName := op.Hash().String()
+ viewName := op.Id().String()
// TODO: me might skip the rendering of blocks that are outside of the view
// but to do that we need to rework how sb.mainSelectableView is maintained
@@ -643,7 +643,7 @@ func (sb *showBug) edit(g *gocui.Gui, v *gocui.View) error {
return nil
}
- op, err := snap.SearchTimelineItem(git.Hash(sb.selected))
+ op, err := snap.SearchTimelineItem(entity.Id(sb.selected))
if err != nil {
return err
}
@@ -651,10 +651,10 @@ func (sb *showBug) edit(g *gocui.Gui, v *gocui.View) error {
switch op.(type) {
case *bug.AddCommentTimelineItem:
message := op.(*bug.AddCommentTimelineItem).Message
- return editCommentWithEditor(sb.bug, op.Hash(), message)
+ return editCommentWithEditor(sb.bug, op.Id(), message)
case *bug.CreateTimelineItem:
preMessage := op.(*bug.CreateTimelineItem).Message
- return editCommentWithEditor(sb.bug, op.Hash(), preMessage)
+ return editCommentWithEditor(sb.bug, op.Id(), preMessage)
case *bug.LabelChangeTimelineItem:
return sb.editLabels(g, snap)
}
diff --git a/termui/termui.go b/termui/termui.go
index 46f9dd06..5d3bb0c1 100644
--- a/termui/termui.go
+++ b/termui/termui.go
@@ -2,11 +2,12 @@
package termui
import (
- "github.com/MichaelMure/git-bug/cache"
- "github.com/MichaelMure/git-bug/input"
- "github.com/MichaelMure/git-bug/util/git"
"github.com/MichaelMure/gocui"
"github.com/pkg/errors"
+
+ "github.com/MichaelMure/git-bug/cache"
+ "github.com/MichaelMure/git-bug/entity"
+ "github.com/MichaelMure/git-bug/input"
)
var errTerminateMainloop = errors.New("terminate gocui mainloop")
@@ -237,7 +238,7 @@ func addCommentWithEditor(bug *cache.BugCache) error {
return errTerminateMainloop
}
-func editCommentWithEditor(bug *cache.BugCache, target git.Hash, preMessage string) error {
+func editCommentWithEditor(bug *cache.BugCache, target entity.Id, preMessage string) error {
// This is somewhat hacky.
// As there is no way to pause gocui, run the editor and restart gocui,
// we have to stop it entirely and start a new one later.
diff --git a/util/git/hash.go b/util/git/hash.go
index d9160d75..d7254178 100644
--- a/util/git/hash.go
+++ b/util/git/hash.go
@@ -5,6 +5,9 @@ import (
"io"
)
+const idLengthSHA1 = 40
+const idLengthSHA256 = 64
+
// Hash is a git hash
type Hash string
@@ -16,7 +19,7 @@ func (h Hash) String() string {
func (h *Hash) UnmarshalGQL(v interface{}) error {
_, ok := v.(string)
if !ok {
- return fmt.Errorf("labels must be strings")
+ return fmt.Errorf("hashes must be strings")
}
*h = v.(Hash)
@@ -35,7 +38,8 @@ func (h Hash) MarshalGQL(w io.Writer) {
// IsValid tell if the hash is valid
func (h *Hash) IsValid() bool {
- if len(*h) != 40 && len(*h) != 64 {
+ // Support for both sha1 and sha256 git hashes
+ if len(*h) != idLengthSHA1 && len(*h) != idLengthSHA256 {
return false
}
for _, r := range *h {