diff options
46 files changed, 552 insertions, 300 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 0d2b1e61..34c88310 100644 --- a/bridge/github/export.go +++ b/bridge/github/export.go @@ -17,6 +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/entity" ) var ( @@ -28,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 @@ -48,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 @@ -102,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) } @@ -112,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 } @@ -165,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()) } @@ -208,7 +209,7 @@ func (ge *githubExporter) exportBug(b *cache.BugCache, since time.Time, out chan out <- core.NewExportBug(b.Id()) // mark bug creation operation as exported - if err := markOperationAsExported(b, createOp.ID(), 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 @@ -227,7 +228,7 @@ func (ge *githubExporter) exportBug(b *cache.BugCache, since time.Time, out chan } // cache operation github id - ge.cachedOperationIDs[createOp.ID()] = bugGithubID + ge.cachedOperationIDs[createOp.Id()] = bugGithubID for _, op := range snapshot.Operations[1:] { // ignore SetMetadata operations @@ -238,15 +239,15 @@ func (ge *githubExporter) exportBug(b *cache.BugCache, since time.Time, out chan // 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[op.ID()] = id - out <- core.NewExportNothing(op.ID(), "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(op.ID(), "missing operation author token") + out <- core.NewExportNothing(op.Id(), "missing operation author token") continue } @@ -263,17 +264,17 @@ func (ge *githubExporter) exportBug(b *cache.BugCache, since time.Time, out chan return } - out <- core.NewExportComment(op.ID()) + out <- core.NewExportComment(op.Id()) // cache comment id - ge.cachedOperationIDs[op.ID()] = id + ge.cachedOperationIDs[op.Id()] = id case *bug.EditCommentOperation: opr := op.(*bug.EditCommentOperation) // Since github doesn't consider the issue body as a comment - if opr.Target == createOp.ID() { + 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 { @@ -282,7 +283,7 @@ func (ge *githubExporter) exportBug(b *cache.BugCache, since time.Time, out chan return } - out <- core.NewExportCommentEdition(op.ID()) + out <- core.NewExportCommentEdition(op.Id()) id = bugGithubID url = bugGithubURL @@ -302,7 +303,7 @@ func (ge *githubExporter) exportBug(b *cache.BugCache, since time.Time, out chan return } - out <- core.NewExportCommentEdition(op.ID()) + out <- core.NewExportCommentEdition(op.Id()) // use comment id/url instead of issue id/url id = eid @@ -317,7 +318,7 @@ func (ge *githubExporter) exportBug(b *cache.BugCache, since time.Time, out chan return } - out <- core.NewExportStatusChange(op.ID()) + out <- core.NewExportStatusChange(op.Id()) id = bugGithubID url = bugGithubURL @@ -330,7 +331,7 @@ func (ge *githubExporter) exportBug(b *cache.BugCache, since time.Time, out chan return } - out <- core.NewExportTitleEdition(op.ID()) + out <- core.NewExportTitleEdition(op.Id()) id = bugGithubID url = bugGithubURL @@ -343,7 +344,7 @@ func (ge *githubExporter) exportBug(b *cache.BugCache, since time.Time, out chan return } - out <- core.NewExportLabelChange(op.ID()) + out <- core.NewExportLabelChange(op.Id()) id = bugGithubID url = bugGithubURL @@ -353,7 +354,7 @@ func (ge *githubExporter) exportBug(b *cache.BugCache, since time.Time, out chan } // mark operation as exported - if err := markOperationAsExported(b, op.ID(), 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 @@ -411,7 +412,7 @@ func getRepositoryNodeID(owner, project, token string) (string, error) { return aux.NodeID, nil } -func markOperationAsExported(b *cache.BugCache, target string, 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 ac18fea5..107fe63b 100644 --- a/bridge/github/export_test.go +++ b/bridge/github/export_test.go @@ -58,13 +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) - _, err = bugWithCommentEditions.EditComment(createOp.ID(), "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) - _, err = bugWithCommentEditions.EditComment(commentOp.ID(), "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 56b50c81..dcaf2d05 100644 --- a/bridge/github/import.go +++ b/bridge/github/import.go @@ -10,7 +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/entity" "github.com/MichaelMure/git-bug/util/text" ) @@ -369,7 +369,7 @@ func (gi *githubImporter) ensureTimelineComment(repo *cache.RepoCache, b *cache. } // set target for the nexr edit now that the comment is created - targetOpID = op.ID() + targetOpID = op.Id() continue } @@ -383,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 string, 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 @@ -445,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 } @@ -488,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 8b389357..08e4c6eb 100644 --- a/bridge/gitlab/import.go +++ b/bridge/gitlab/import.go @@ -10,7 +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/entity" "github.com/MichaelMure/git-bug/util/text" ) @@ -324,7 +324,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 } @@ -29,18 +29,12 @@ const editClockEntryPattern = "edit-clock-%d" var ErrBugNotExist = errors.New("bug doesn't exist") -type ErrMultipleMatch struct { - Matching []entity.Id +func NewErrMultipleMatchBug(matching []entity.Id) *entity.ErrMultipleMatch { + return entity.NewErrMultipleMatch("bug", 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 bug found:\n%s", strings.Join(matching, "\n")) +func NewErrMultipleMatchOp(matching []entity.Id) *entity.ErrMultipleMatch { + return entity.NewErrMultipleMatch("operation", matching) } var _ Interface = &Bug{} @@ -100,7 +94,7 @@ 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]) diff --git a/bug/bug_actions.go b/bug/bug_actions.go index e6fc12d5..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) diff --git a/bug/op_add_comment_test.go b/bug/op_add_comment_test.go index 55bafe36..60364cf1 100644 --- a/bug/op_add_comment_test.go +++ b/bug/op_add_comment_test.go @@ -2,12 +2,12 @@ package bug import ( "encoding/json" - "fmt" "testing" "time" - "github.com/MichaelMure/git-bug/identity" "github.com/stretchr/testify/assert" + + "github.com/MichaelMure/git-bug/identity" ) func TestAddCommentSerialize(t *testing.T) { diff --git a/bug/snapshot.go b/bug/snapshot.go index a937200f..39366c6d 100644 --- a/bug/snapshot.go +++ b/bug/snapshot.go @@ -66,9 +66,9 @@ func (snap *Snapshot) SearchTimelineItem(id entity.Id) (TimelineItem, error) { } // SearchComment will search for a comment matching the given hash -func (snap *Snapshot) SearchComment(id string) (*Comment, error) { +func (snap *Snapshot) SearchComment(id entity.Id) (*Comment, error) { for _, c := range snap.Comments { - if c.id.String() == id { + if c.id == id { return &c, nil } } diff --git a/cache/bug_cache.go b/cache/bug_cache.go index cb102737..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,45 +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 []string -} - -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) (string, error) { +func (c *BugCache) ResolveOperationWithMetadata(key string, value string) (entity.Id, error) { // preallocate but empty - matching := make([]string, 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 { - matching = append(matching, op.ID()) + matching = append(matching, op.Id()) } } @@ -76,7 +58,7 @@ func (c *BugCache) ResolveOperationWithMetadata(key string, value string) (strin } if len(matching) > 1 { - return "", ErrMultipleMatchOp{Matching: matching} + return "", bug.NewErrMultipleMatchOp(matching) } return matching[0], nil @@ -228,7 +210,7 @@ func (c *BugCache) SetTitleRaw(author *IdentityCache, unixTime int64, title stri return op, c.notifyUpdated() } -func (c *BugCache) EditComment(target string, 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 @@ -237,7 +219,7 @@ func (c *BugCache) EditComment(target string, message string) (*bug.EditCommentO return c.EditCommentRaw(author, time.Now().Unix(), target, message, nil) } -func (c *BugCache) EditCommentRaw(author *IdentityCache, unixTime int64, target string, 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 @@ -250,7 +232,7 @@ func (c *BugCache) EditCommentRaw(author *IdentityCache, unixTime int64, target return op, c.notifyUpdated() } -func (c *BugCache) SetMetadata(target string, 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 @@ -259,7 +241,7 @@ func (c *BugCache) SetMetadata(target string, newMetadata map[string]string) (*b return c.SetMetadataRaw(author, time.Now().Unix(), target, newMetadata) } -func (c *BugCache) SetMetadataRaw(author *IdentityCache, unixTime int64, target string, 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 8efc11e2..e053f9e4 100644 --- a/cache/bug_excerpt.go +++ b/cache/bug_excerpt.go @@ -18,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 entity.ID + Id entity.Id CreateLamportTime lamport.Time EditLamportTime lamport.Time @@ -29,14 +29,14 @@ type BugExcerpt struct { Labels []bug.Label Title string LenComments int - Actors []entity.ID - Participants []entity.ID + 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 } @@ -61,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() } @@ -101,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 4ec3cb0e..fdc87154 100644 --- a/commands/select/select.go +++ b/commands/select/select.go @@ -11,6 +11,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/repository" ) @@ -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") } - id := string(buf) - if !bug.IDIsValid(id) { + 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") 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 index 9356433d..7022305c 100644 --- a/entity/err.go +++ b/entity/err.go @@ -1 +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/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 eaefc671..c052e57b 100644 --- a/graphql/graph/gen_graph.go +++ b/graphql/graph/gen_graph.go @@ -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 @@ -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) } @@ -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 { @@ -2918,7 +2943,7 @@ func (ec *executionContext) _AddCommentOperation_id(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.ID(), nil + return ec.resolvers.AddCommentOperation().ID(rctx, obj) }) if err != nil { ec.Error(ctx, err) @@ -3211,7 +3236,7 @@ func (ec *executionContext) _AddCommentTimelineItem_id(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.ID(), nil + return ec.resolvers.AddCommentTimelineItem().ID(rctx, obj) }) if err != nil { ec.Error(ctx, err) @@ -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) @@ -5195,7 +5220,7 @@ func (ec *executionContext) _CreateOperation_id(ctx context.Context, field graph 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.CreateOperation().ID(rctx, obj) }) if err != nil { ec.Error(ctx, err) @@ -5417,7 +5442,7 @@ func (ec *executionContext) _CreateTimelineItem_id(ctx context.Context, field gr 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.CreateTimelineItem().ID(rctx, obj) }) if err != nil { ec.Error(ctx, err) @@ -5750,7 +5775,7 @@ func (ec *executionContext) _EditCommentOperation_id(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.ID(), nil + return ec.resolvers.EditCommentOperation().ID(rctx, obj) }) if err != nil { ec.Error(ctx, err) @@ -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) @@ -6552,7 +6577,7 @@ func (ec *executionContext) _LabelChangeOperation_id(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.ID(), nil + return ec.resolvers.LabelChangeOperation().ID(rctx, obj) }) if err != nil { ec.Error(ctx, err) @@ -6811,7 +6836,7 @@ func (ec *executionContext) _LabelChangeTimelineItem_id(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.ID(), nil + return ec.resolvers.LabelChangeTimelineItem().ID(rctx, obj) }) if err != nil { ec.Error(ctx, err) @@ -8325,7 +8350,7 @@ func (ec *executionContext) _SetStatusOperation_id(ctx context.Context, field gr 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.SetStatusOperation().ID(rctx, obj) }) if err != nil { ec.Error(ctx, err) @@ -8473,7 +8498,7 @@ func (ec *executionContext) _SetStatusTimelineItem_id(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.ID(), nil + return ec.resolvers.SetStatusTimelineItem().ID(rctx, obj) }) if err != nil { ec.Error(ctx, err) @@ -8621,7 +8646,7 @@ func (ec *executionContext) _SetTitleOperation_id(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.ID(), nil + return ec.resolvers.SetTitleOperation().ID(rctx, obj) }) if err != nil { ec.Error(ctx, err) @@ -8914,7 +8939,7 @@ func (ec *executionContext) _SetTitleTimelineItem_id(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.ID(), nil + return ec.resolvers.SetTitleTimelineItem().ID(rctx, obj) }) if err != nil { ec.Error(ctx, err) @@ -10838,10 +10863,19 @@ func (ec *executionContext) _AddCommentOperation(ctx context.Context, sel ast.Se case "__typename": out.Values[i] = graphql.MarshalString("AddCommentOperation") case "id": - out.Values[i] = ec._AddCommentOperation_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._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 { @@ -10928,10 +10962,19 @@ func (ec *executionContext) _AddCommentTimelineItem(ctx context.Context, sel ast case "__typename": out.Values[i] = graphql.MarshalString("AddCommentTimelineItem") case "id": - out.Values[i] = ec._AddCommentTimelineItem_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._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) { @@ -11584,10 +11645,19 @@ func (ec *executionContext) _CreateOperation(ctx context.Context, sel ast.Select case "__typename": out.Values[i] = graphql.MarshalString("CreateOperation") case "id": - out.Values[i] = ec._CreateOperation_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._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 { @@ -11645,10 +11715,19 @@ func (ec *executionContext) _CreateTimelineItem(ctx context.Context, sel ast.Sel case "__typename": out.Values[i] = graphql.MarshalString("CreateTimelineItem") case "id": - out.Values[i] = ec._CreateTimelineItem_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._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 { @@ -11730,10 +11809,19 @@ func (ec *executionContext) _EditCommentOperation(ctx context.Context, sel ast.S case "__typename": out.Values[i] = graphql.MarshalString("EditCommentOperation") case "id": - out.Values[i] = ec._EditCommentOperation_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._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 { @@ -12037,10 +12134,19 @@ func (ec *executionContext) _LabelChangeOperation(ctx context.Context, sel ast.S case "__typename": out.Values[i] = graphql.MarshalString("LabelChangeOperation") case "id": - out.Values[i] = ec._LabelChangeOperation_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._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 { @@ -12134,10 +12240,19 @@ func (ec *executionContext) _LabelChangeTimelineItem(ctx context.Context, sel as case "__typename": out.Values[i] = graphql.MarshalString("LabelChangeTimelineItem") case "id": - out.Values[i] = ec._LabelChangeTimelineItem_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._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 { @@ -12589,10 +12704,19 @@ func (ec *executionContext) _SetStatusOperation(ctx context.Context, sel ast.Sel case "__typename": out.Values[i] = graphql.MarshalString("SetStatusOperation") case "id": - out.Values[i] = ec._SetStatusOperation_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._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 { @@ -12649,10 +12773,19 @@ func (ec *executionContext) _SetStatusTimelineItem(ctx context.Context, sel ast. case "__typename": out.Values[i] = graphql.MarshalString("SetStatusTimelineItem") case "id": - out.Values[i] = ec._SetStatusTimelineItem_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._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 { @@ -12709,10 +12842,19 @@ func (ec *executionContext) _SetTitleOperation(ctx context.Context, sel ast.Sele case "__typename": out.Values[i] = graphql.MarshalString("SetTitleOperation") case "id": - out.Values[i] = ec._SetTitleOperation_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._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 { @@ -12799,10 +12941,19 @@ func (ec *executionContext) _SetTitleTimelineItem(ctx context.Context, sel ast.S case "__typename": out.Values[i] = graphql.MarshalString("SetTitleTimelineItem") case "id": - out.Values[i] = ec._SetTitleTimelineItem_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._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..3080c98b 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) { + panic("implement me") +} + 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/identity/bare.go b/identity/bare.go index 93cea91f..a243f074 100644 --- a/identity/bare.go +++ b/identity/bare.go @@ -50,13 +50,12 @@ type bareIdentityJSON struct { } func (i *Bare) MarshalJSON() ([]byte, error) { - data, err := json.Marshal(bareIdentityJSON{ + return json.Marshal(bareIdentityJSON{ Name: i.name, Email: i.email, Login: i.login, AvatarUrl: i.avatarUrl, }) - return data, err } func (i *Bare) UnmarshalJSON(data []byte) error { diff --git a/identity/common.go b/identity/common.go index 68a6d57c..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 diff --git a/identity/identity_actions.go b/identity/identity_actions.go index 40aab8e2..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) 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 d25d21db..228b85b0 100644 --- a/termui/show_bug.go +++ b/termui/show_bug.go @@ -7,6 +7,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/util/colors" "github.com/MichaelMure/git-bug/util/text" "github.com/MichaelMure/gocui" @@ -213,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()), @@ -231,7 +232,7 @@ func (sb *showBug) renderMain(g *gocui.Gui, mainView *gocui.View) error { y0 += lines + 1 for _, op := range snap.Timeline { - viewName := op.ID() + 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 @@ -642,7 +643,7 @@ func (sb *showBug) edit(g *gocui.Gui, v *gocui.View) error { return nil } - op, err := snap.SearchTimelineItem(sb.selected) + op, err := snap.SearchTimelineItem(entity.Id(sb.selected)) if err != nil { return err } @@ -650,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.ID(), message) + return editCommentWithEditor(sb.bug, op.Id(), message) case *bug.CreateTimelineItem: preMessage := op.(*bug.CreateTimelineItem).Message - return editCommentWithEditor(sb.bug, op.ID(), 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 2a0b64e9..5d3bb0c1 100644 --- a/termui/termui.go +++ b/termui/termui.go @@ -6,6 +6,7 @@ import ( "github.com/pkg/errors" "github.com/MichaelMure/git-bug/cache" + "github.com/MichaelMure/git-bug/entity" "github.com/MichaelMure/git-bug/input" ) @@ -237,7 +238,7 @@ func addCommentWithEditor(bug *cache.BugCache) error { return errTerminateMainloop } -func editCommentWithEditor(bug *cache.BugCache, target string, 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. |