diff options
35 files changed, 1791 insertions, 1276 deletions
@@ -677,6 +677,3 @@ func (bug *Bug) Compile() Snapshot { return snap } - -// Sign post method for gqlgen -func (bug *Bug) IsAuthored() {} diff --git a/bug/op_add_comment.go b/bug/op_add_comment.go index e16ea0dd..78c8847a 100644 --- a/bug/op_add_comment.go +++ b/bug/op_add_comment.go @@ -21,6 +21,9 @@ type AddCommentOperation struct { Files []git.Hash `json:"files"` } +// Sign-post method for gqlgen +func (op *AddCommentOperation) IsOperation() {} + func (op *AddCommentOperation) base() *OpBase { return &op.OpBase } diff --git a/bug/op_create.go b/bug/op_create.go index 0da95d4d..b2af438b 100644 --- a/bug/op_create.go +++ b/bug/op_create.go @@ -22,6 +22,9 @@ type CreateOperation struct { Files []git.Hash `json:"files"` } +// Sign-post method for gqlgen +func (op *CreateOperation) IsOperation() {} + func (op *CreateOperation) base() *OpBase { return &op.OpBase } diff --git a/bug/op_edit_comment.go b/bug/op_edit_comment.go index 48020421..44ee5877 100644 --- a/bug/op_edit_comment.go +++ b/bug/op_edit_comment.go @@ -24,6 +24,9 @@ type EditCommentOperation struct { Files []git.Hash `json:"files"` } +// Sign-post method for gqlgen +func (op *EditCommentOperation) IsOperation() {} + func (op *EditCommentOperation) base() *OpBase { return &op.OpBase } diff --git a/bug/op_label_change.go b/bug/op_label_change.go index c911de26..fefe2402 100644 --- a/bug/op_label_change.go +++ b/bug/op_label_change.go @@ -21,6 +21,9 @@ type LabelChangeOperation struct { Removed []Label `json:"removed"` } +// Sign-post method for gqlgen +func (op *LabelChangeOperation) IsOperation() {} + func (op *LabelChangeOperation) base() *OpBase { return &op.OpBase } diff --git a/bug/op_noop.go b/bug/op_noop.go index 16d32297..6364f918 100644 --- a/bug/op_noop.go +++ b/bug/op_noop.go @@ -16,6 +16,9 @@ type NoOpOperation struct { OpBase } +// Sign-post method for gqlgen +func (op *NoOpOperation) IsOperation() {} + func (op *NoOpOperation) base() *OpBase { return &op.OpBase } diff --git a/bug/op_set_metadata.go b/bug/op_set_metadata.go index 67f7e009..23d11461 100644 --- a/bug/op_set_metadata.go +++ b/bug/op_set_metadata.go @@ -17,6 +17,9 @@ type SetMetadataOperation struct { NewMetadata map[string]string `json:"new_metadata"` } +// Sign-post method for gqlgen +func (op *SetMetadataOperation) IsOperation() {} + func (op *SetMetadataOperation) base() *OpBase { return &op.OpBase } diff --git a/bug/op_set_status.go b/bug/op_set_status.go index 8a245184..eb2c0ba4 100644 --- a/bug/op_set_status.go +++ b/bug/op_set_status.go @@ -18,6 +18,9 @@ type SetStatusOperation struct { Status Status `json:"status"` } +// Sign-post method for gqlgen +func (op *SetStatusOperation) IsOperation() {} + func (op *SetStatusOperation) base() *OpBase { return &op.OpBase } diff --git a/bug/op_set_title.go b/bug/op_set_title.go index fadd29a9..ddd98f0e 100644 --- a/bug/op_set_title.go +++ b/bug/op_set_title.go @@ -21,6 +21,9 @@ type SetTitleOperation struct { Was string `json:"was"` } +// Sign-post method for gqlgen +func (op *SetTitleOperation) IsOperation() {} + func (op *SetTitleOperation) base() *OpBase { return &op.OpBase } diff --git a/bug/operation.go b/bug/operation.go index dd95e096..20d44f6c 100644 --- a/bug/operation.go +++ b/bug/operation.go @@ -52,6 +52,9 @@ type Operation interface { AllMetadata() map[string]string // GetAuthor return the author identity GetAuthor() identity.Interface + + // sign-post method for gqlgen + IsOperation() } func deriveId(data []byte) entity.Id { diff --git a/cache/filter.go b/cache/filter.go index 9b1de1d5..f840067b 100644 --- a/cache/filter.go +++ b/cache/filter.go @@ -4,10 +4,17 @@ import ( "strings" "github.com/MichaelMure/git-bug/bug" + "github.com/MichaelMure/git-bug/entity" ) +// resolver has the resolving functions needed by filters. +// This exist mainly to go through the functions of the cache with proper locking. +type resolver interface { + ResolveIdentityExcerpt(id entity.Id) (*IdentityExcerpt, error) +} + // Filter is a predicate that match a subset of bugs -type Filter func(repoCache *RepoCache, excerpt *BugExcerpt) bool +type Filter func(excerpt *BugExcerpt, resolver resolver) bool // StatusFilter return a Filter that match a bug status func StatusFilter(query string) (Filter, error) { @@ -16,21 +23,21 @@ func StatusFilter(query string) (Filter, error) { return nil, err } - return func(repoCache *RepoCache, excerpt *BugExcerpt) bool { + return func(excerpt *BugExcerpt, resolver resolver) bool { return excerpt.Status == status }, nil } // AuthorFilter return a Filter that match a bug author func AuthorFilter(query string) Filter { - return func(repoCache *RepoCache, excerpt *BugExcerpt) bool { + return func(excerpt *BugExcerpt, resolver resolver) bool { query = strings.ToLower(query) // Normal identity if excerpt.AuthorId != "" { - author, ok := repoCache.identitiesExcerpts[excerpt.AuthorId] - if !ok { - panic("missing identity in the cache") + author, err := resolver.ResolveIdentityExcerpt(excerpt.AuthorId) + if err != nil { + panic(err) } return author.Match(query) @@ -43,7 +50,7 @@ func AuthorFilter(query string) Filter { // LabelFilter return a Filter that match a label func LabelFilter(label string) Filter { - return func(repoCache *RepoCache, excerpt *BugExcerpt) bool { + return func(excerpt *BugExcerpt, resolver resolver) bool { for _, l := range excerpt.Labels { if string(l) == label { return true @@ -55,13 +62,13 @@ func LabelFilter(label string) Filter { // ActorFilter return a Filter that match a bug actor func ActorFilter(query string) Filter { - return func(repoCache *RepoCache, excerpt *BugExcerpt) bool { + return func(excerpt *BugExcerpt, resolver resolver) bool { query = strings.ToLower(query) for _, id := range excerpt.Actors { - identityExcerpt, ok := repoCache.identitiesExcerpts[id] - if !ok { - panic("missing identity in the cache") + identityExcerpt, err := resolver.ResolveIdentityExcerpt(id) + if err != nil { + panic(err) } if identityExcerpt.Match(query) { @@ -74,13 +81,13 @@ func ActorFilter(query string) Filter { // ParticipantFilter return a Filter that match a bug participant func ParticipantFilter(query string) Filter { - return func(repoCache *RepoCache, excerpt *BugExcerpt) bool { + return func(excerpt *BugExcerpt, resolver resolver) bool { query = strings.ToLower(query) for _, id := range excerpt.Participants { - identityExcerpt, ok := repoCache.identitiesExcerpts[id] - if !ok { - panic("missing identity in the cache") + identityExcerpt, err := resolver.ResolveIdentityExcerpt(id) + if err != nil { + panic(err) } if identityExcerpt.Match(query) { @@ -93,7 +100,7 @@ func ParticipantFilter(query string) Filter { // TitleFilter return a Filter that match if the title contains the given query func TitleFilter(query string) Filter { - return func(repo *RepoCache, excerpt *BugExcerpt) bool { + return func(excerpt *BugExcerpt, resolver resolver) bool { return strings.Contains( strings.ToLower(excerpt.Title), strings.ToLower(query), @@ -103,7 +110,7 @@ func TitleFilter(query string) Filter { // NoLabelFilter return a Filter that match the absence of labels func NoLabelFilter() Filter { - return func(repoCache *RepoCache, excerpt *BugExcerpt) bool { + return func(excerpt *BugExcerpt, resolver resolver) bool { return len(excerpt.Labels) == 0 } } @@ -120,32 +127,32 @@ type Filters struct { } // Match check if a bug match the set of filters -func (f *Filters) Match(repoCache *RepoCache, excerpt *BugExcerpt) bool { - if match := f.orMatch(f.Status, repoCache, excerpt); !match { +func (f *Filters) Match(excerpt *BugExcerpt, resolver resolver) bool { + if match := f.orMatch(f.Status, excerpt, resolver); !match { return false } - if match := f.orMatch(f.Author, repoCache, excerpt); !match { + if match := f.orMatch(f.Author, excerpt, resolver); !match { return false } - if match := f.orMatch(f.Participant, repoCache, excerpt); !match { + if match := f.orMatch(f.Participant, excerpt, resolver); !match { return false } - if match := f.orMatch(f.Actor, repoCache, excerpt); !match { + if match := f.orMatch(f.Actor, excerpt, resolver); !match { return false } - if match := f.andMatch(f.Label, repoCache, excerpt); !match { + if match := f.andMatch(f.Label, excerpt, resolver); !match { return false } - if match := f.andMatch(f.NoFilters, repoCache, excerpt); !match { + if match := f.andMatch(f.NoFilters, excerpt, resolver); !match { return false } - if match := f.andMatch(f.Title, repoCache, excerpt); !match { + if match := f.andMatch(f.Title, excerpt, resolver); !match { return false } @@ -153,28 +160,28 @@ func (f *Filters) Match(repoCache *RepoCache, excerpt *BugExcerpt) bool { } // Check if any of the filters provided match the bug -func (*Filters) orMatch(filters []Filter, repoCache *RepoCache, excerpt *BugExcerpt) bool { +func (*Filters) orMatch(filters []Filter, excerpt *BugExcerpt, resolver resolver) bool { if len(filters) == 0 { return true } match := false for _, f := range filters { - match = match || f(repoCache, excerpt) + match = match || f(excerpt, resolver) } return match } // Check if all of the filters provided match the bug -func (*Filters) andMatch(filters []Filter, repoCache *RepoCache, excerpt *BugExcerpt) bool { +func (*Filters) andMatch(filters []Filter, excerpt *BugExcerpt, resolver resolver) bool { if len(filters) == 0 { return true } match := true for _, f := range filters { - match = match && f(repoCache, excerpt) + match = match && f(excerpt, resolver) } return match diff --git a/cache/filter_test.go b/cache/filter_test.go index a47d2ad7..2c7fabc0 100644 --- a/cache/filter_test.go +++ b/cache/filter_test.go @@ -28,7 +28,7 @@ func TestTitleFilter(t *testing.T) { t.Run(tt.name, func(t *testing.T) { filter := TitleFilter(tt.query) excerpt := &BugExcerpt{Title: tt.title} - assert.Equal(t, tt.match, filter(nil, excerpt)) + assert.Equal(t, tt.match, filter(excerpt, nil)) }) } } diff --git a/cache/repo_cache.go b/cache/repo_cache.go index 18be9b5a..c8d6bd69 100644 --- a/cache/repo_cache.go +++ b/cache/repo_cache.go @@ -10,6 +10,7 @@ import ( "path" "sort" "strconv" + "sync" "time" "github.com/pkg/errors" @@ -57,11 +58,13 @@ type RepoCache struct { // the underlying repo repo repository.ClockedRepo + muBug sync.RWMutex // excerpt of bugs data for all bugs bugExcerpts map[entity.Id]*BugExcerpt // bug loaded in memory bugs map[entity.Id]*BugCache + muIdentity sync.RWMutex // excerpt of identities data for all identities identitiesExcerpts map[entity.Id]*IdentityExcerpt // identities loaded in memory @@ -157,6 +160,11 @@ func (c *RepoCache) lock() error { } func (c *RepoCache) Close() error { + c.muBug.Lock() + defer c.muBug.Unlock() + c.muIdentity.Lock() + defer c.muIdentity.Unlock() + c.identities = make(map[entity.Id]*IdentityCache) c.identitiesExcerpts = nil c.bugs = make(map[entity.Id]*BugCache) @@ -169,12 +177,16 @@ 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 entity.Id) error { + c.muBug.Lock() + b, ok := c.bugs[id] if !ok { + c.muBug.Unlock() panic("missing bug in the cache") } c.bugExcerpts[id] = NewBugExcerpt(b.bug, b.Snapshot()) + c.muBug.Unlock() // we only need to write the bug cache return c.writeBugCache() @@ -183,12 +195,16 @@ func (c *RepoCache) bugUpdated(id entity.Id) 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 entity.Id) error { + c.muIdentity.Lock() + i, ok := c.identities[id] if !ok { + c.muIdentity.Unlock() panic("missing identity in the cache") } c.identitiesExcerpts[id] = NewIdentityExcerpt(i.Identity) + c.muIdentity.Unlock() // we only need to write the identity cache return c.writeIdentityCache() @@ -205,6 +221,9 @@ func (c *RepoCache) load() error { // load will try to read from the disk the bug cache file func (c *RepoCache) loadBugCache() error { + c.muBug.Lock() + defer c.muBug.Unlock() + f, err := os.Open(bugCacheFilePath(c.repo)) if err != nil { return err @@ -234,6 +253,9 @@ func (c *RepoCache) loadBugCache() error { // load will try to read from the disk the identity cache file func (c *RepoCache) loadIdentityCache() error { + c.muIdentity.Lock() + defer c.muIdentity.Unlock() + f, err := os.Open(identityCacheFilePath(c.repo)) if err != nil { return err @@ -272,6 +294,9 @@ func (c *RepoCache) write() error { // write will serialize on disk the bug cache file func (c *RepoCache) writeBugCache() error { + c.muBug.RLock() + defer c.muBug.RUnlock() + var data bytes.Buffer aux := struct { @@ -304,6 +329,9 @@ func (c *RepoCache) writeBugCache() error { // write will serialize on disk the identity cache file func (c *RepoCache) writeIdentityCache() error { + c.muIdentity.RLock() + defer c.muIdentity.RUnlock() + var data bytes.Buffer aux := struct { @@ -343,6 +371,11 @@ func identityCacheFilePath(repo repository.Repo) string { } func (c *RepoCache) buildCache() error { + c.muBug.Lock() + defer c.muBug.Unlock() + c.muIdentity.Lock() + defer c.muIdentity.Unlock() + _, _ = fmt.Fprintf(os.Stderr, "Building identity cache... ") c.identitiesExcerpts = make(map[entity.Id]*IdentityExcerpt) @@ -378,9 +411,24 @@ func (c *RepoCache) buildCache() error { return nil } +// ResolveBugExcerpt retrieve a BugExcerpt matching the exact given id +func (c *RepoCache) ResolveBugExcerpt(id entity.Id) (*BugExcerpt, error) { + c.muBug.RLock() + defer c.muBug.RUnlock() + + e, ok := c.bugExcerpts[id] + if !ok { + return nil, bug.ErrBugNotExist + } + + return e, nil +} + // ResolveBug retrieve a bug matching the exact given id func (c *RepoCache) ResolveBug(id entity.Id) (*BugCache, error) { + c.muBug.RLock() cached, ok := c.bugs[id] + c.muBug.RUnlock() if ok { return cached, nil } @@ -391,19 +439,20 @@ func (c *RepoCache) ResolveBug(id entity.Id) (*BugCache, error) { } cached = NewBugCache(c, b) + + c.muBug.Lock() c.bugs[id] = cached + c.muBug.Unlock() return cached, nil } -// ResolveBugExcerpt retrieve a BugExcerpt matching the exact given id -func (c *RepoCache) ResolveBugExcerpt(id entity.Id) (*BugExcerpt, error) { - e, ok := c.bugExcerpts[id] - if !ok { - return nil, bug.ErrBugNotExist - } - - return e, nil +// ResolveBugExcerptPrefix retrieve a BugExcerpt matching an id prefix. It fails if multiple +// bugs match. +func (c *RepoCache) ResolveBugExcerptPrefix(prefix string) (*BugExcerpt, error) { + return c.ResolveBugExcerptMatcher(func(excerpt *BugExcerpt) bool { + return excerpt.Id.HasPrefix(prefix) + }) } // ResolveBugPrefix retrieve a bug matching an id prefix. It fails if multiple @@ -423,7 +472,26 @@ func (c *RepoCache) ResolveBugCreateMetadata(key string, value string) (*BugCach }) } +func (c *RepoCache) ResolveBugExcerptMatcher(f func(*BugExcerpt) bool) (*BugExcerpt, error) { + id, err := c.resolveBugMatcher(f) + if err != nil { + return nil, err + } + return c.ResolveBugExcerpt(id) +} + func (c *RepoCache) ResolveBugMatcher(f func(*BugExcerpt) bool) (*BugCache, error) { + id, err := c.resolveBugMatcher(f) + if err != nil { + return nil, err + } + return c.ResolveBug(id) +} + +func (c *RepoCache) resolveBugMatcher(f func(*BugExcerpt) bool) (entity.Id, error) { + c.muBug.RLock() + defer c.muBug.RUnlock() + // preallocate but empty matching := make([]entity.Id, 0, 5) @@ -434,18 +502,21 @@ func (c *RepoCache) ResolveBugMatcher(f func(*BugExcerpt) bool) (*BugCache, erro } if len(matching) > 1 { - return nil, bug.NewErrMultipleMatchBug(matching) + return entity.UnsetId, bug.NewErrMultipleMatchBug(matching) } if len(matching) == 0 { - return nil, bug.ErrBugNotExist + return entity.UnsetId, bug.ErrBugNotExist } - return c.ResolveBug(matching[0]) + return matching[0], nil } // QueryBugs return the id of all Bug matching the given Query func (c *RepoCache) QueryBugs(query *Query) []entity.Id { + c.muBug.RLock() + defer c.muBug.RUnlock() + if query == nil { return c.AllBugsIds() } @@ -453,7 +524,7 @@ func (c *RepoCache) QueryBugs(query *Query) []entity.Id { var filtered []*BugExcerpt for _, excerpt := range c.bugExcerpts { - if query.Match(c, excerpt) { + if query.Match(excerpt, c) { filtered = append(filtered, excerpt) } } @@ -488,6 +559,9 @@ func (c *RepoCache) QueryBugs(query *Query) []entity.Id { // AllBugsIds return all known bug ids func (c *RepoCache) AllBugsIds() []entity.Id { + c.muBug.RLock() + defer c.muBug.RUnlock() + result := make([]entity.Id, len(c.bugExcerpts)) i := 0 @@ -505,6 +579,9 @@ func (c *RepoCache) AllBugsIds() []entity.Id { // labels are defined in a configuration file. Until that, the default behavior // is to return the list of labels already used. func (c *RepoCache) ValidLabels() []bug.Label { + c.muBug.RLock() + defer c.muBug.RUnlock() + set := map[bug.Label]interface{}{} for _, excerpt := range c.bugExcerpts { @@ -564,12 +641,15 @@ func (c *RepoCache) NewBugRaw(author *IdentityCache, unixTime int64, title strin return nil, nil, err } + c.muBug.Lock() if _, has := c.bugs[b.Id()]; has { + c.muBug.Unlock() return nil, nil, fmt.Errorf("bug %s already exist in the cache", b.Id()) } cached := NewBugCache(c, b) c.bugs[b.Id()] = cached + c.muBug.Unlock() // force the write of the excerpt err = c.bugUpdated(b.Id()) @@ -615,7 +695,9 @@ func (c *RepoCache) MergeAll(remote string) <-chan entity.MergeResult { switch result.Status { case entity.MergeStatusNew, entity.MergeStatusUpdated: i := result.Entity.(*identity.Identity) + c.muIdentity.Lock() c.identitiesExcerpts[result.Id] = NewIdentityExcerpt(i) + c.muIdentity.Unlock() } } @@ -631,7 +713,9 @@ func (c *RepoCache) MergeAll(remote string) <-chan entity.MergeResult { case entity.MergeStatusNew, entity.MergeStatusUpdated: b := result.Entity.(*bug.Bug) snap := b.Compile() + c.muBug.Lock() c.bugExcerpts[result.Id] = NewBugExcerpt(b, &snap) + c.muBug.Unlock() } } @@ -745,9 +829,24 @@ func repoIsAvailable(repo repository.Repo) error { return nil } +// ResolveIdentityExcerpt retrieve a IdentityExcerpt matching the exact given id +func (c *RepoCache) ResolveIdentityExcerpt(id entity.Id) (*IdentityExcerpt, error) { + c.muIdentity.RLock() + defer c.muIdentity.RUnlock() + + e, ok := c.identitiesExcerpts[id] + if !ok { + return nil, identity.ErrIdentityNotExist + } + + return e, nil +} + // ResolveIdentity retrieve an identity matching the exact given id func (c *RepoCache) ResolveIdentity(id entity.Id) (*IdentityCache, error) { + c.muIdentity.RLock() cached, ok := c.identities[id] + c.muIdentity.RUnlock() if ok { return cached, nil } @@ -758,19 +857,20 @@ func (c *RepoCache) ResolveIdentity(id entity.Id) (*IdentityCache, error) { } cached = NewIdentityCache(c, i) + + c.muIdentity.Lock() c.identities[id] = cached + c.muIdentity.Unlock() return cached, nil } -// ResolveIdentityExcerpt retrieve a IdentityExcerpt matching the exact given id -func (c *RepoCache) ResolveIdentityExcerpt(id entity.Id) (*IdentityExcerpt, error) { - e, ok := c.identitiesExcerpts[id] - if !ok { - return nil, identity.ErrIdentityNotExist - } - - return e, nil +// ResolveIdentityExcerptPrefix retrieve a IdentityExcerpt matching an id prefix. +// It fails if multiple identities match. +func (c *RepoCache) ResolveIdentityExcerptPrefix(prefix string) (*IdentityExcerpt, error) { + return c.ResolveIdentityExcerptMatcher(func(excerpt *IdentityExcerpt) bool { + return excerpt.Id.HasPrefix(prefix) + }) } // ResolveIdentityPrefix retrieve an Identity matching an id prefix. @@ -789,7 +889,26 @@ func (c *RepoCache) ResolveIdentityImmutableMetadata(key string, value string) ( }) } +func (c *RepoCache) ResolveIdentityExcerptMatcher(f func(*IdentityExcerpt) bool) (*IdentityExcerpt, error) { + id, err := c.resolveIdentityMatcher(f) + if err != nil { + return nil, err + } + return c.ResolveIdentityExcerpt(id) +} + func (c *RepoCache) ResolveIdentityMatcher(f func(*IdentityExcerpt) bool) (*IdentityCache, error) { + id, err := c.resolveIdentityMatcher(f) + if err != nil { + return nil, err + } + return c.ResolveIdentity(id) +} + +func (c *RepoCache) resolveIdentityMatcher(f func(*IdentityExcerpt) bool) (entity.Id, error) { + c.muIdentity.RLock() + defer c.muIdentity.RUnlock() + // preallocate but empty matching := make([]entity.Id, 0, 5) @@ -800,18 +919,21 @@ func (c *RepoCache) ResolveIdentityMatcher(f func(*IdentityExcerpt) bool) (*Iden } if len(matching) > 1 { - return nil, identity.NewErrMultipleMatch(matching) + return entity.UnsetId, identity.NewErrMultipleMatch(matching) } if len(matching) == 0 { - return nil, identity.ErrIdentityNotExist + return entity.UnsetId, identity.ErrIdentityNotExist } - return c.ResolveIdentity(matching[0]) + return matching[0], nil } // AllIdentityIds return all known identity ids func (c *RepoCache) AllIdentityIds() []entity.Id { + c.muIdentity.RLock() + defer c.muIdentity.RUnlock() + result := make([]entity.Id, len(c.identitiesExcerpts)) i := 0 @@ -829,6 +951,9 @@ func (c *RepoCache) SetUserIdentity(i *IdentityCache) error { return err } + c.muIdentity.RLock() + defer c.muIdentity.RUnlock() + // Make sure that everything is fine if _, ok := c.identities[i.Id()]; !ok { panic("SetUserIdentity while the identity is not from the cache, something is wrong") @@ -847,6 +972,9 @@ func (c *RepoCache) GetUserIdentity() (*IdentityCache, error) { } } + c.muIdentity.Lock() + defer c.muIdentity.Unlock() + i, err := identity.GetUserIdentity(c.repo) if err != nil { return nil, err @@ -859,6 +987,25 @@ func (c *RepoCache) GetUserIdentity() (*IdentityCache, error) { return cached, nil } +func (c *RepoCache) GetUserIdentityExcerpt() (*IdentityExcerpt, error) { + if c.userIdentityId == "" { + id, err := identity.GetUserIdentityId(c.repo) + if err != nil { + return nil, err + } + c.userIdentityId = id + } + + c.muIdentity.RLock() + defer c.muIdentity.RUnlock() + + excerpt, ok := c.identitiesExcerpts[c.userIdentityId] + if !ok { + return nil, fmt.Errorf("cache: missing identity excerpt %v", c.userIdentityId) + } + return excerpt, nil +} + func (c *RepoCache) IsUserIdentitySet() (bool, error) { return identity.IsUserIdentitySet(c.repo) } @@ -902,12 +1049,14 @@ func (c *RepoCache) finishIdentity(i *identity.Identity, metadata map[string]str return nil, err } + c.muIdentity.Lock() if _, has := c.identities[i.Id()]; has { return nil, fmt.Errorf("identity %s already exist in the cache", i.Id()) } cached := NewIdentityCache(c, i) c.identities[i.Id()] = cached + c.muIdentity.Unlock() // force the write of the excerpt err = c.identityUpdated(i.Id()) @@ -3,7 +3,7 @@ module github.com/MichaelMure/git-bug go 1.11 require ( - github.com/99designs/gqlgen v0.10.3-0.20200205113530-b941b970f0b6 + github.com/99designs/gqlgen v0.10.3-0.20200209012558-b7a58a1c0e4b github.com/MichaelMure/go-term-text v0.2.6 github.com/araddon/dateparse v0.0.0-20190622164848-0fb0a474d195 github.com/awesome-gocui/gocui v0.6.1-0.20191115151952-a34ffb055986 @@ -1,11 +1,11 @@ -github.com/99designs/gqlgen v0.10.3-0.20200205113530-b941b970f0b6 h1:WU+9Z7AoCyl+HlB2kE0O+4/3BaVqLQfnX5dHigV5p8A= -github.com/99designs/gqlgen v0.10.3-0.20200205113530-b941b970f0b6/go.mod h1:28v/ATDVwPUriwNtAIrQEhRHXJjdi5dVGqxSqPna1I8= +github.com/99designs/gqlgen v0.10.3-0.20200208093655-ab8d62b67dd0 h1:ADy3XJwhOYg6Pb90XeXazWvO+9gpOsgLuaM1buZUZOY= +github.com/99designs/gqlgen v0.10.3-0.20200208093655-ab8d62b67dd0/go.mod h1:dfBhwZKMcSYiYRMTs8qWF+Oha6782e1xPfgRmVal9I8= +github.com/99designs/gqlgen v0.10.3-0.20200209012558-b7a58a1c0e4b h1:510xa84qGbDemwTHNio4cLWkdKFxxJgVtsIOH+Ku8bo= +github.com/99designs/gqlgen v0.10.3-0.20200209012558-b7a58a1c0e4b/go.mod h1:dfBhwZKMcSYiYRMTs8qWF+Oha6782e1xPfgRmVal9I8= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/Masterminds/glide v0.13.2/go.mod h1:STyF5vcenH/rUqTEv+/hBXlSTo7KYwg2oc2f4tzPWic= github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/Masterminds/vcs v1.13.0/go.mod h1:N09YCmOQr6RLxC6UNHzuVwAdodYbbnycGHSmwVJjcKA= -github.com/MichaelMure/go-term-text v0.2.4 h1:h+lAsjG5o3oNvaJeh7OzE8zdSiB/VyJo9JzSnncrZv4= -github.com/MichaelMure/go-term-text v0.2.4/go.mod h1:o2Z5T3b28F4kwAojGvvNdbzjHf9t18vbQ7E2pmTe2Ww= github.com/MichaelMure/go-term-text v0.2.6 h1:dSmJSzk2iI5xWymSMrMbdVM1bxYWu3DjDFhdcJvAuqA= github.com/MichaelMure/go-term-text v0.2.6/go.mod h1:o2Z5T3b28F4kwAojGvvNdbzjHf9t18vbQ7E2pmTe2Ww= github.com/agnivade/levenshtein v1.0.1 h1:3oJU7J3FGFmyhn8KHjmVaZCN5hxTr7GxgRue+sxIXdQ= @@ -36,8 +36,6 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= -github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= @@ -69,6 +67,7 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/matryer/moq v0.0.0-20200106131100-75d0ddfc0007/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ= github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA= @@ -132,6 +131,8 @@ github.com/vektah/dataloaden v0.2.1-0.20190515034641-a19b9a6e7c9e/go.mod h1:/HUd github.com/vektah/gqlparser v1.2.1 h1:C+L7Go/eUbN0w6Y0kaiq2W6p2wN5j8wU82EdDXxDivc= github.com/vektah/gqlparser v1.2.1/go.mod h1:bkVf0FX+Stjg/MHnm8mEyubuaArhNEqfQhF+OTiAL74= github.com/vektah/gqlparser v1.3.1 h1:8b0IcD3qZKWJQHSzynbDlrtP3IxVydZ2DZepCGofqfU= +github.com/vektah/gqlparser v1.3.1 h1:8b0IcD3qZKWJQHSzynbDlrtP3IxVydZ2DZepCGofqfU= +github.com/vektah/gqlparser v1.3.1/go.mod h1:bkVf0FX+Stjg/MHnm8mEyubuaArhNEqfQhF+OTiAL74= github.com/vektah/gqlparser v1.3.1/go.mod h1:bkVf0FX+Stjg/MHnm8mEyubuaArhNEqfQhF+OTiAL74= github.com/xanzy/go-gitlab v0.22.1 h1:TVxgHmoa35jQL+9FCkG0nwPDxU9dQZXknBTDtGaSFno= github.com/xanzy/go-gitlab v0.22.1/go.mod h1:t4Bmvnxj7k37S4Y17lfLx+nLqkf/oQwT2HagfWKv5Og= diff --git a/graphql/connections/connections.go b/graphql/connections/connections.go index 82ad2514..0083f8b2 100644 --- a/graphql/connections/connections.go +++ b/graphql/connections/connections.go @@ -1,6 +1,6 @@ //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_identity.go gen "Name=Identity NodeType=models.IdentityWrapper 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" //go:generate genny -in=connection_template.go -out=gen_timeline.go gen "Name=TimelineItem NodeType=bug.TimelineItem EdgeType=models.TimelineItemEdge ConnectionType=models.TimelineItemConnection" diff --git a/graphql/connections/lazy_identity.go b/graphql/connections/edges.go index 3274dd7e..4e37fcd9 100644 --- a/graphql/connections/lazy_identity.go +++ b/graphql/connections/edges.go @@ -2,6 +2,17 @@ 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 entity.Id + Cursor string +} + +// GetCursor return the cursor of a LazyBugEdge +func (lbe LazyBugEdge) GetCursor() string { + return lbe.Cursor +} + // LazyIdentityEdge is a special relay edge used to implement a lazy loading connection type LazyIdentityEdge struct { Id entity.Id diff --git a/graphql/connections/gen_identity.go b/graphql/connections/gen_identity.go index b52b6f96..061e8936 100644 --- a/graphql/connections/gen_identity.go +++ b/graphql/connections/gen_identity.go @@ -8,23 +8,22 @@ import ( "fmt" "github.com/MichaelMure/git-bug/graphql/models" - "github.com/MichaelMure/git-bug/identity" ) -// IdentityInterfaceEdgeMaker define a function that take a identity.Interface and an offset and +// ModelsIdentityWrapperEdgeMaker define a function that take a models.IdentityWrapper and an offset and // create an Edge. -type IdentityEdgeMaker func(value identity.Interface, offset int) Edge +type IdentityEdgeMaker func(value models.IdentityWrapper, offset int) Edge // IdentityConMaker define a function that create a models.IdentityConnection type IdentityConMaker func( edges []*models.IdentityEdge, - nodes []identity.Interface, + nodes []models.IdentityWrapper, info *models.PageInfo, totalCount int) (*models.IdentityConnection, error) // IdentityCon will paginate a source according to the input of a relay connection -func IdentityCon(source []identity.Interface, edgeMaker IdentityEdgeMaker, conMaker IdentityConMaker, input models.ConnectionInput) (*models.IdentityConnection, error) { - var nodes []identity.Interface +func IdentityCon(source []models.IdentityWrapper, edgeMaker IdentityEdgeMaker, conMaker IdentityConMaker, input models.ConnectionInput) (*models.IdentityConnection, error) { + var nodes []models.IdentityWrapper var edges []*models.IdentityEdge var cursors []string var pageInfo = &models.PageInfo{} diff --git a/graphql/connections/lazy_bug.go b/graphql/connections/lazy_bug.go deleted file mode 100644 index 00692e8b..00000000 --- a/graphql/connections/lazy_bug.go +++ /dev/null @@ -1,14 +0,0 @@ -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 entity.Id - Cursor string -} - -// GetCursor return the cursor of a LazyBugEdge -func (lbe LazyBugEdge) GetCursor() string { - return lbe.Cursor -} diff --git a/graphql/gqlgen.yml b/graphql/gqlgen.yml index fc27cc2d..fe33b8c7 100644 --- a/graphql/gqlgen.yml +++ b/graphql/gqlgen.yml @@ -10,13 +10,24 @@ models: RepositoryMutation: model: github.com/MichaelMure/git-bug/graphql/models.RepositoryMutation Bug: - model: github.com/MichaelMure/git-bug/bug.Snapshot + model: github.com/MichaelMure/git-bug/graphql/models.BugWrapper + fields: + actors: + resolver: true + participants: + resolver: true + comments: + resolver: true + timeline: + resolver: true + operations: + resolver: true Color: model: image/color.RGBA Comment: model: github.com/MichaelMure/git-bug/bug.Comment Identity: - model: github.com/MichaelMure/git-bug/identity.Interface + model: github.com/MichaelMure/git-bug/graphql/models.IdentityWrapper Label: model: github.com/MichaelMure/git-bug/bug.Label Hash: diff --git a/graphql/graph/gen_graph.go b/graphql/graph/gen_graph.go index 5e882142..67913377 100644 --- a/graphql/graph/gen_graph.go +++ b/graphql/graph/gen_graph.go @@ -17,7 +17,6 @@ import ( "github.com/99designs/gqlgen/graphql/introspection" "github.com/MichaelMure/git-bug/bug" "github.com/MichaelMure/git-bug/graphql/models" - "github.com/MichaelMure/git-bug/identity" "github.com/MichaelMure/git-bug/util/git" "github.com/vektah/gqlparser" "github.com/vektah/gqlparser/ast" @@ -45,6 +44,7 @@ type ResolverRoot interface { AddCommentTimelineItem() AddCommentTimelineItemResolver Bug() BugResolver Color() ColorResolver + Comment() CommentResolver CommentHistoryStep() CommentHistoryStepResolver CreateOperation() CreateOperationResolver CreateTimelineItem() CreateTimelineItemResolver @@ -370,55 +370,59 @@ type ComplexityRoot struct { type AddCommentOperationResolver interface { ID(ctx context.Context, obj *bug.AddCommentOperation) (string, error) - + Author(ctx context.Context, obj *bug.AddCommentOperation) (models.IdentityWrapper, error) Date(ctx context.Context, obj *bug.AddCommentOperation) (*time.Time, error) } type AddCommentTimelineItemResolver interface { ID(ctx context.Context, obj *bug.AddCommentTimelineItem) (string, error) + Author(ctx context.Context, obj *bug.AddCommentTimelineItem) (models.IdentityWrapper, 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) + ID(ctx context.Context, obj models.BugWrapper) (string, error) + HumanID(ctx context.Context, obj models.BugWrapper) (string, error) + Status(ctx context.Context, obj models.BugWrapper) (models.Status, error) - LastEdit(ctx context.Context, obj *bug.Snapshot) (*time.Time, error) - Actors(ctx context.Context, obj *bug.Snapshot, after *string, before *string, first *int, last *int) (*models.IdentityConnection, error) - Participants(ctx context.Context, obj *bug.Snapshot, after *string, before *string, first *int, last *int) (*models.IdentityConnection, error) - Comments(ctx context.Context, obj *bug.Snapshot, after *string, before *string, first *int, last *int) (*models.CommentConnection, error) - Timeline(ctx context.Context, obj *bug.Snapshot, after *string, before *string, first *int, last *int) (*models.TimelineItemConnection, error) - Operations(ctx context.Context, obj *bug.Snapshot, after *string, before *string, first *int, last *int) (*models.OperationConnection, error) + Actors(ctx context.Context, obj models.BugWrapper, after *string, before *string, first *int, last *int) (*models.IdentityConnection, error) + Participants(ctx context.Context, obj models.BugWrapper, after *string, before *string, first *int, last *int) (*models.IdentityConnection, error) + Comments(ctx context.Context, obj models.BugWrapper, after *string, before *string, first *int, last *int) (*models.CommentConnection, error) + Timeline(ctx context.Context, obj models.BugWrapper, after *string, before *string, first *int, last *int) (*models.TimelineItemConnection, error) + Operations(ctx context.Context, obj models.BugWrapper, after *string, before *string, first *int, last *int) (*models.OperationConnection, error) } type ColorResolver interface { R(ctx context.Context, obj *color.RGBA) (int, error) G(ctx context.Context, obj *color.RGBA) (int, error) B(ctx context.Context, obj *color.RGBA) (int, error) } +type CommentResolver interface { + Author(ctx context.Context, obj *bug.Comment) (models.IdentityWrapper, error) +} 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) - + Author(ctx context.Context, obj *bug.CreateOperation) (models.IdentityWrapper, error) Date(ctx context.Context, obj *bug.CreateOperation) (*time.Time, error) } type CreateTimelineItemResolver interface { ID(ctx context.Context, obj *bug.CreateTimelineItem) (string, error) + Author(ctx context.Context, obj *bug.CreateTimelineItem) (models.IdentityWrapper, 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) - + Author(ctx context.Context, obj *bug.EditCommentOperation) (models.IdentityWrapper, 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) - HumanID(ctx context.Context, obj identity.Interface) (string, error) + ID(ctx context.Context, obj models.IdentityWrapper) (string, error) + HumanID(ctx context.Context, obj models.IdentityWrapper) (string, error) } type LabelResolver interface { Name(ctx context.Context, obj *bug.Label) (string, error) @@ -426,7 +430,7 @@ type LabelResolver interface { } type LabelChangeOperationResolver interface { ID(ctx context.Context, obj *bug.LabelChangeOperation) (string, error) - + Author(ctx context.Context, obj *bug.LabelChangeOperation) (models.IdentityWrapper, error) Date(ctx context.Context, obj *bug.LabelChangeOperation) (*time.Time, error) } type LabelChangeResultResolver interface { @@ -434,7 +438,7 @@ type LabelChangeResultResolver interface { } type LabelChangeTimelineItemResolver interface { ID(ctx context.Context, obj *bug.LabelChangeTimelineItem) (string, error) - + Author(ctx context.Context, obj *bug.LabelChangeTimelineItem) (models.IdentityWrapper, error) Date(ctx context.Context, obj *bug.LabelChangeTimelineItem) (*time.Time, error) } type MutationResolver interface { @@ -453,32 +457,32 @@ type QueryResolver interface { } type RepositoryResolver interface { AllBugs(ctx context.Context, obj *models.Repository, after *string, before *string, first *int, last *int, query *string) (*models.BugConnection, error) - Bug(ctx context.Context, obj *models.Repository, prefix string) (*bug.Snapshot, error) + Bug(ctx context.Context, obj *models.Repository, prefix string) (models.BugWrapper, error) AllIdentities(ctx context.Context, obj *models.Repository, after *string, before *string, first *int, last *int) (*models.IdentityConnection, error) - Identity(ctx context.Context, obj *models.Repository, prefix string) (identity.Interface, error) - UserIdentity(ctx context.Context, obj *models.Repository) (identity.Interface, error) + Identity(ctx context.Context, obj *models.Repository, prefix string) (models.IdentityWrapper, error) + UserIdentity(ctx context.Context, obj *models.Repository) (models.IdentityWrapper, error) ValidLabels(ctx context.Context, obj *models.Repository, after *string, before *string, first *int, last *int) (*models.LabelConnection, error) } type SetStatusOperationResolver interface { ID(ctx context.Context, obj *bug.SetStatusOperation) (string, error) - + Author(ctx context.Context, obj *bug.SetStatusOperation) (models.IdentityWrapper, 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) - + Author(ctx context.Context, obj *bug.SetStatusTimelineItem) (models.IdentityWrapper, 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) - + Author(ctx context.Context, obj *bug.SetTitleOperation) (models.IdentityWrapper, error) Date(ctx context.Context, obj *bug.SetTitleOperation) (*time.Time, error) } type SetTitleTimelineItemResolver interface { ID(ctx context.Context, obj *bug.SetTitleTimelineItem) (string, error) - + Author(ctx context.Context, obj *bug.SetTitleTimelineItem) (models.IdentityWrapper, error) Date(ctx context.Context, obj *bug.SetTitleTimelineItem) (*time.Time, error) } @@ -1873,877 +1877,642 @@ func (ec *executionContext) introspectType(name string) (*introspection.Type, er return introspection.WrapTypeFromDef(parsedSchema, parsedSchema.Types[name]), nil } -var parsedSchema = gqlparser.MustLoadSchema( - &ast.Source{Name: "schema.graphql", Input: `input AddCommentInput { - """ - A unique identifier for the client performing the mutation. - """ - clientMutationId: String - """ - "The name of the repository. If not set, the default repository is used. - """ - repoRef: String - """ - The bug ID's prefix. - """ - prefix: String! - """ - The first message of the new bug. - """ - message: String! - """ - The collection of file's hash required for the first message. - """ - files: [Hash!] +var sources = []*ast.Source{ + &ast.Source{Name: "schema/bug.graphql", Input: `"""Represents a comment on a bug.""" +type Comment implements Authored { + """The author of this comment.""" + author: Identity! + + """The message of this comment.""" + message: String! + + """All media's hash referenced in this comment""" + files: [Hash!]! } -type AddCommentOperation implements Operation & Authored { - """ - The identifier of the operation - """ - id: String! - """ - The author of this object. - """ - author: Identity! - """ - The datetime when this operation was issued. - """ - date: Time! - message: String! - files: [Hash!]! + +type CommentConnection { + edges: [CommentEdge!]! + nodes: [Comment!]! + pageInfo: PageInfo! + totalCount: Int! } -type AddCommentPayload { - """ - A unique identifier for the client performing the mutation. - """ - clientMutationId: String - """ - The affected bug. - """ - bug: Bug! - """ - The resulting operation. - """ - operation: AddCommentOperation! -} -""" -AddCommentTimelineItem is a TimelineItem that represent a Comment and its edition history -""" -type AddCommentTimelineItem implements TimelineItem & Authored { - """ - The identifier of the source operation - """ - id: String! - author: Identity! - message: String! - messageIsEmpty: Boolean! - files: [Hash!]! - createdAt: Time! - lastEdit: Time! - edited: Boolean! - history: [CommentHistoryStep!]! -} -""" -An object that has an author. -""" -interface Authored { - """ - The author of this object. - """ - author: Identity! + +type CommentEdge { + cursor: String! + node: Comment! } + +enum Status { + OPEN + CLOSED +} + type Bug implements Authored { - """ - The identifier for this bug - """ - id: String! - """ - The human version (truncated) identifier for this bug - """ - humanId: String! - status: Status! - title: String! - labels: [Label!]! - author: Identity! - createdAt: Time! - lastEdit: Time! - """ - The actors of the bug. Actors are Identity that have interacted with the bug. - """ - actors(""" - Returns the elements in the list that come after the specified cursor. - """ - after: String, """ - Returns the elements in the list that come before the specified cursor. - """ - before: String, """ - Returns the first _n_ elements from the list. - """ - first: Int, """ - Returns the last _n_ elements from the list. - """ - last: Int): IdentityConnection! - """ - The participants of the bug. Participants are Identity that have created or - added a comment on the bug. - """ - participants(""" - Returns the elements in the list that come after the specified cursor. - """ - after: String, """ - Returns the elements in the list that come before the specified cursor. - """ - before: String, """ - Returns the first _n_ elements from the list. - """ - first: Int, """ - Returns the last _n_ elements from the list. - """ - last: Int): IdentityConnection! - comments(""" - Returns the elements in the list that come after the specified cursor. - """ - after: String, """ - Returns the elements in the list that come before the specified cursor. - """ - before: String, """ - Returns the first _n_ elements from the list. - """ - first: Int, """ - Returns the last _n_ elements from the list. - """ - last: Int): CommentConnection! - timeline(""" - Returns the elements in the list that come after the specified cursor. - """ - after: String, """ - Returns the elements in the list that come before the specified cursor. - """ - before: String, """ - Returns the first _n_ elements from the list. - """ - first: Int, """ - Returns the last _n_ elements from the list. - """ - last: Int): TimelineItemConnection! - operations(""" - Returns the elements in the list that come after the specified cursor. - """ - after: String, """ - Returns the elements in the list that come before the specified cursor. - """ - before: String, """ - Returns the first _n_ elements from the list. - """ - first: Int, """ - Returns the last _n_ elements from the list. - """ - last: Int): OperationConnection! -} -""" -The connection type for Bug. -""" + """The identifier for this bug""" + id: String! + """The human version (truncated) identifier for this bug""" + humanId: String! + status: Status! + title: String! + labels: [Label!]! + author: Identity! + createdAt: Time! + lastEdit: Time! + + """The actors of the bug. Actors are Identity that have interacted with the bug.""" + actors( + """Returns the elements in the list that come after the specified cursor.""" + after: String + """Returns the elements in the list that come before the specified cursor.""" + before: String + """Returns the first _n_ elements from the list.""" + first: Int + """Returns the last _n_ elements from the list.""" + last: Int + ): IdentityConnection! + + """The participants of the bug. Participants are Identity that have created or + added a comment on the bug.""" + participants( + """Returns the elements in the list that come after the specified cursor.""" + after: String + """Returns the elements in the list that come before the specified cursor.""" + before: String + """Returns the first _n_ elements from the list.""" + first: Int + """Returns the last _n_ elements from the list.""" + last: Int + ): IdentityConnection! + + comments( + """Returns the elements in the list that come after the specified cursor.""" + after: String + """Returns the elements in the list that come before the specified cursor.""" + before: String + """Returns the first _n_ elements from the list.""" + first: Int + """Returns the last _n_ elements from the list.""" + last: Int + ): CommentConnection! + + timeline( + """Returns the elements in the list that come after the specified cursor.""" + after: String + """Returns the elements in the list that come before the specified cursor.""" + before: String + """Returns the first _n_ elements from the list.""" + first: Int + """Returns the last _n_ elements from the list.""" + last: Int + ): TimelineItemConnection! + + operations( + """Returns the elements in the list that come after the specified cursor.""" + after: String + """Returns the elements in the list that come before the specified cursor.""" + before: String + """Returns the first _n_ elements from the list.""" + first: Int + """Returns the last _n_ elements from the list.""" + last: Int + ): OperationConnection! +} + +"""The connection type for Bug.""" type BugConnection { - """ - A list of edges. - """ - edges: [BugEdge!]! - nodes: [Bug!]! - """ - Information to aid in pagination. - """ - pageInfo: PageInfo! - """ - Identifies the total count of items in the connection. - """ - totalCount: Int! -} -""" -An edge in a connection. -""" + """A list of edges.""" + edges: [BugEdge!]! + nodes: [Bug!]! + """Information to aid in pagination.""" + pageInfo: PageInfo! + """Identifies the total count of items in the connection.""" + totalCount: Int! +} + +"""An edge in a connection.""" type BugEdge { - """ - A cursor for use in pagination. - """ - cursor: String! - """ - The item at the end of the edge. - """ - node: Bug! + """A cursor for use in pagination.""" + cursor: String! + """The item at the end of the edge.""" + node: Bug! } +`, BuiltIn: false}, + &ast.Source{Name: "schema/identity.graphql", Input: `"""Represents an identity""" +type Identity { + """The identifier for this identity""" + id: String! + """The human version (truncated) identifier for this identity""" + humanId: String! + """The name of the person, if known.""" + name: String + """The email of the person, if known.""" + email: String + """A non-empty string to display, representing the identity, based on the non-empty values.""" + displayName: String! + """An url to an avatar""" + avatarUrl: String + """isProtected is true if the chain of git commits started to be signed. + If that's the case, only signed commit with a valid key for this identity can be added.""" + isProtected: Boolean! +} + +type IdentityConnection { + edges: [IdentityEdge!]! + nodes: [Identity!]! + pageInfo: PageInfo! + totalCount: Int! +} + +type IdentityEdge { + cursor: String! + node: Identity! +}`, BuiltIn: false}, + &ast.Source{Name: "schema/label.graphql", Input: `"""Label for a bug.""" +type Label { + """The name of the label.""" + name: String! + """Color of the label.""" + color: Color! +} + +type LabelConnection { + edges: [LabelEdge!]! + nodes: [Label!]! + pageInfo: PageInfo! + totalCount: Int! +} + +type LabelEdge { + cursor: String! + node: Label! +}`, BuiltIn: false}, + &ast.Source{Name: "schema/mutations.graphql", Input: `input NewBugInput { + """A unique identifier for the client performing the mutation.""" + clientMutationId: String + """"The name of the repository. If not set, the default repository is used.""" + repoRef: String + """The title of the new bug.""" + title: String! + """The first message of the new bug.""" + message: String! + """The collection of file's hash required for the first message.""" + files: [Hash!] +} + +type NewBugPayload { + """A unique identifier for the client performing the mutation.""" + clientMutationId: String + """The created bug.""" + bug: Bug! + """The resulting operation.""" + operation: CreateOperation! +} + +input AddCommentInput { + """A unique identifier for the client performing the mutation.""" + clientMutationId: String + """"The name of the repository. If not set, the default repository is used.""" + repoRef: String + """The bug ID's prefix.""" + prefix: String! + """The first message of the new bug.""" + message: String! + """The collection of file's hash required for the first message.""" + files: [Hash!] +} + +type AddCommentPayload { + """A unique identifier for the client performing the mutation.""" + clientMutationId: String + """The affected bug.""" + bug: Bug! + """The resulting operation.""" + operation: AddCommentOperation! +} + input ChangeLabelInput { - """ - A unique identifier for the client performing the mutation. - """ - clientMutationId: String - """ - "The name of the repository. If not set, the default repository is used. - """ - repoRef: String - """ - The bug ID's prefix. - """ - prefix: String! - """ - The list of label to add. - """ - added: [String!] - """ - The list of label to remove. - """ - Removed: [String!] + """A unique identifier for the client performing the mutation.""" + clientMutationId: String + """"The name of the repository. If not set, the default repository is used.""" + repoRef: String + """The bug ID's prefix.""" + prefix: String! + """The list of label to add.""" + added: [String!] + """The list of label to remove.""" + Removed: [String!] } -type ChangeLabelPayload { - """ - A unique identifier for the client performing the mutation. - """ - clientMutationId: String - """ - The affected bug. - """ - bug: Bug! - """ - The resulting operation. - """ - operation: LabelChangeOperation! - """ - The effect each source label had. - """ - results: [LabelChangeResult]! + +enum LabelChangeStatus { + ADDED + REMOVED + DUPLICATE_IN_OP + ALREADY_EXIST + DOESNT_EXIST } -input CloseBugInput { - """ - A unique identifier for the client performing the mutation. - """ - clientMutationId: String - """ - "The name of the repository. If not set, the default repository is used. - """ - repoRef: String - """ - The bug ID's prefix. - """ - prefix: String! + +type LabelChangeResult { + """The source label.""" + label: Label! + """The effect this label had.""" + status: LabelChangeStatus! } -type CloseBugPayload { - """ - A unique identifier for the client performing the mutation. - """ - clientMutationId: String - """ - The affected bug. - """ - bug: Bug! - """ - The resulting operation. - """ - operation: SetStatusOperation! -} -""" -Defines a color by red, green and blue components. -""" -type Color { - """ - Red component of the color. - """ - R: Int! - """ - Green component of the color. - """ - G: Int! - """ - Blue component of the color. - """ - B: Int! -} -""" -Represents a comment on a bug. -""" -type Comment implements Authored { - """ - The author of this comment. - """ - author: Identity! - """ - The message of this comment. - """ - message: String! - """ - All media's hash referenced in this comment - """ - files: [Hash!]! + +type ChangeLabelPayload { + """A unique identifier for the client performing the mutation.""" + clientMutationId: String + """The affected bug.""" + bug: Bug! + """The resulting operation.""" + operation: LabelChangeOperation! + """The effect each source label had.""" + results: [LabelChangeResult]! } -type CommentConnection { - edges: [CommentEdge!]! - nodes: [Comment!]! - pageInfo: PageInfo! - totalCount: Int! + +input OpenBugInput { + """A unique identifier for the client performing the mutation.""" + clientMutationId: String + """"The name of the repository. If not set, the default repository is used.""" + repoRef: String + """The bug ID's prefix.""" + prefix: String! } -type CommentEdge { - cursor: String! - node: Comment! + +type OpenBugPayload { + """A unique identifier for the client performing the mutation.""" + clientMutationId: String + """The affected bug.""" + bug: Bug! + """The resulting operation.""" + operation: SetStatusOperation! } -""" -CommentHistoryStep hold one version of a message in the history -""" -type CommentHistoryStep { - message: String! - date: Time! + +input CloseBugInput { + """A unique identifier for the client performing the mutation.""" + clientMutationId: String + """"The name of the repository. If not set, the default repository is used.""" + repoRef: String + """The bug ID's prefix.""" + prefix: String! } -input CommitAsNeededInput { - """ - A unique identifier for the client performing the mutation. - """ - clientMutationId: String - """ - "The name of the repository. If not set, the default repository is used. - """ - repoRef: String - """ - The bug ID's prefix. - """ - prefix: String! + +type CloseBugPayload { + """A unique identifier for the client performing the mutation.""" + clientMutationId: String + """The affected bug.""" + bug: Bug! + """The resulting operation.""" + operation: SetStatusOperation! } -type CommitAsNeededPayload { - """ - A unique identifier for the client performing the mutation. - """ - clientMutationId: String - """ - The affected bug. - """ - bug: Bug! + +input SetTitleInput { + """A unique identifier for the client performing the mutation.""" + clientMutationId: String + """"The name of the repository. If not set, the default repository is used.""" + repoRef: String + """The bug ID's prefix.""" + prefix: String! + """The new title.""" + title: String! +} + +type SetTitlePayload { + """A unique identifier for the client performing the mutation.""" + clientMutationId: String + """The affected bug.""" + bug: Bug! + """The resulting operation""" + operation: SetTitleOperation! } + input CommitInput { - """ - A unique identifier for the client performing the mutation. - """ - clientMutationId: String - """ - "The name of the repository. If not set, the default repository is used. - """ - repoRef: String - """ - The bug ID's prefix. - """ - prefix: String! + """A unique identifier for the client performing the mutation.""" + clientMutationId: String + """"The name of the repository. If not set, the default repository is used.""" + repoRef: String + """The bug ID's prefix.""" + prefix: String! } + type CommitPayload { - """ - A unique identifier for the client performing the mutation. - """ - clientMutationId: String - """ - The affected bug. - """ - bug: Bug! + """A unique identifier for the client performing the mutation.""" + clientMutationId: String + """The affected bug.""" + bug: Bug! } -type CreateOperation implements Operation & Authored { - """ - The identifier of the operation - """ - id: String! - """ - The author of this object. - """ - author: Identity! - """ - The datetime when this operation was issued. - """ - date: Time! - title: String! - message: String! - files: [Hash!]! -} -""" -CreateTimelineItem is a TimelineItem that represent the creation of a bug and its message edition history -""" -type CreateTimelineItem implements TimelineItem & Authored { - """ - The identifier of the source operation - """ - id: String! - author: Identity! - message: String! - messageIsEmpty: Boolean! - files: [Hash!]! - createdAt: Time! - lastEdit: Time! - edited: Boolean! - history: [CommentHistoryStep!]! + +input CommitAsNeededInput { + """A unique identifier for the client performing the mutation.""" + clientMutationId: String + """"The name of the repository. If not set, the default repository is used.""" + repoRef: String + """The bug ID's prefix.""" + prefix: String! } -type EditCommentOperation implements Operation & Authored { - """ - The identifier of the operation - """ - id: String! - """ - The author of this object. - """ - author: Identity! - """ - The datetime when this operation was issued. - """ - date: Time! - target: String! - message: String! - files: [Hash!]! + +type CommitAsNeededPayload { + """A unique identifier for the client performing the mutation.""" + clientMutationId: String + """The affected bug.""" + bug: Bug! } -scalar Hash -""" -Represents an identity -""" -type Identity { - """ - The identifier for this identity - """ - id: String! - """ - The human version (truncated) identifier for this identity - """ - humanId: String! - """ - The name of the person, if known. - """ - name: String - """ - The email of the person, if known. - """ - email: String - """ - A non-empty string to display, representing the identity, based on the non-empty values. - """ - displayName: String! - """ - An url to an avatar - """ - avatarUrl: String - """ - isProtected is true if the chain of git commits started to be signed. - If that's the case, only signed commit with a valid key for this identity can be added. - """ - isProtected: Boolean! +`, BuiltIn: false}, + &ast.Source{Name: "schema/operations.graphql", Input: `"""An operation applied to a bug.""" +interface Operation { + """The identifier of the operation""" + id: String! + """The operations author.""" + author: Identity! + """The datetime when this operation was issued.""" + date: Time! } -type IdentityConnection { - edges: [IdentityEdge!]! - nodes: [Identity!]! - pageInfo: PageInfo! - totalCount: Int! + +# Connection + +"""The connection type for an Operation""" +type OperationConnection { + edges: [OperationEdge!]! + nodes: [Operation!]! + pageInfo: PageInfo! + totalCount: Int! } -type IdentityEdge { - cursor: String! - node: Identity! + +"""Represent an Operation""" +type OperationEdge { + cursor: String! + node: Operation! } -""" -Label for a bug. -""" -type Label { - """ - The name of the label. - """ - name: String! - """ - Color of the label. - """ - color: Color! + +# Operations + +type CreateOperation implements Operation & Authored { + """The identifier of the operation""" + id: String! + """The author of this object.""" + author: Identity! + """The datetime when this operation was issued.""" + date: Time! + + title: String! + message: String! + files: [Hash!]! } -type LabelChangeOperation implements Operation & Authored { - """ - The identifier of the operation - """ - id: String! - """ - The author of this object. - """ - author: Identity! - """ - The datetime when this operation was issued. - """ - date: Time! - added: [Label!]! - removed: [Label!]! + +type SetTitleOperation implements Operation & Authored { + """The identifier of the operation""" + id: String! + """The author of this object.""" + author: Identity! + """The datetime when this operation was issued.""" + date: Time! + + title: String! + was: String! } -type LabelChangeResult { - """ - The source label. - """ - label: Label! - """ - The effect this label had. - """ - status: LabelChangeStatus! + +type AddCommentOperation implements Operation & Authored { + """The identifier of the operation""" + id: String! + """The author of this object.""" + author: Identity! + """The datetime when this operation was issued.""" + date: Time! + + message: String! + files: [Hash!]! } -enum LabelChangeStatus { - ADDED - REMOVED - DUPLICATE_IN_OP - ALREADY_EXIST - DOESNT_EXIST -} -""" -LabelChangeTimelineItem is a TimelineItem that represent a change in the labels of a bug -""" -type LabelChangeTimelineItem implements TimelineItem & Authored { - """ - The identifier of the source operation - """ - id: String! - author: Identity! - date: Time! - added: [Label!]! - removed: [Label!]! + +type EditCommentOperation implements Operation & Authored { + """The identifier of the operation""" + id: String! + """The author of this object.""" + author: Identity! + """The datetime when this operation was issued.""" + date: Time! + + target: String! + message: String! + files: [Hash!]! } -type LabelConnection { - edges: [LabelEdge!]! - nodes: [Label!]! - pageInfo: PageInfo! - totalCount: Int! + +type SetStatusOperation implements Operation & Authored { + """The identifier of the operation""" + id: String! + """The author of this object.""" + author: Identity! + """The datetime when this operation was issued.""" + date: Time! + + status: Status! } -type LabelEdge { - cursor: String! - node: Label! + +type LabelChangeOperation implements Operation & Authored { + """The identifier of the operation""" + id: String! + """The author of this object.""" + author: Identity! + """The datetime when this operation was issued.""" + date: Time! + + added: [Label!]! + removed: [Label!]! +} +`, BuiltIn: false}, + &ast.Source{Name: "schema/repository.graphql", Input: ` +type Repository { + """All the bugs""" + allBugs( + """Returns the elements in the list that come after the specified cursor.""" + after: String + """Returns the elements in the list that come before the specified cursor.""" + before: String + """Returns the first _n_ elements from the list.""" + first: Int + """Returns the last _n_ elements from the list.""" + last: Int + """A query to select and order bugs""" + query: String + ): BugConnection! + + bug(prefix: String!): Bug + + """All the identities""" + allIdentities( + """Returns the elements in the list that come after the specified cursor.""" + after: String + """Returns the elements in the list that come before the specified cursor.""" + before: String + """Returns the first _n_ elements from the list.""" + first: Int + """Returns the last _n_ elements from the list.""" + last: Int + ): IdentityConnection! + + identity(prefix: String!): Identity + + """The identity created or selected by the user as its own""" + userIdentity: Identity + + """List of valid labels.""" + validLabels( + """Returns the elements in the list that come after the specified cursor.""" + after: String + """Returns the elements in the list that come before the specified cursor.""" + before: String + """Returns the first _n_ elements from the list.""" + first: Int + """Returns the last _n_ elements from the list.""" + last: Int + ): LabelConnection! +}`, BuiltIn: false}, + &ast.Source{Name: "schema/root.graphql", Input: `type Query { + """The default unnamend repository.""" + defaultRepository: Repository + """Access a repository by reference/name.""" + repository(ref: String!): Repository + + #TODO: connection for all repositories } + type Mutation { - """ - Create a new bug - """ - newBug(input: NewBugInput!): NewBugPayload! - """ - Add a new comment to a bug - """ - addComment(input: AddCommentInput!): AddCommentPayload! - """ - Add or remove a set of label on a bug - """ - changeLabels(input: ChangeLabelInput): ChangeLabelPayload! - """ - Change a bug's status to open - """ - openBug(input: OpenBugInput!): OpenBugPayload! - """ - Change a bug's status to closed - """ - closeBug(input: CloseBugInput!): CloseBugPayload! - """ - Change a bug's title - """ - setTitle(input: SetTitleInput!): SetTitlePayload! - """ - Commit write the pending operations into storage. This mutation fail if nothing is pending - """ - commit(input: CommitInput!): CommitPayload! - """ - Commit write the pending operations into storage. This mutation succed if nothing is pending - """ - commitAsNeeded(input: CommitAsNeededInput!): CommitAsNeededPayload! -} -input NewBugInput { - """ - A unique identifier for the client performing the mutation. - """ - clientMutationId: String - """ - "The name of the repository. If not set, the default repository is used. - """ - repoRef: String - """ - The title of the new bug. - """ - title: String! - """ - The first message of the new bug. - """ - message: String! - """ - The collection of file's hash required for the first message. - """ - files: [Hash!] -} -type NewBugPayload { - """ - A unique identifier for the client performing the mutation. - """ - clientMutationId: String - """ - The created bug. - """ - bug: Bug! - """ - The resulting operation. - """ - operation: CreateOperation! + """Create a new bug""" + newBug(input: NewBugInput!): NewBugPayload! + """Add a new comment to a bug""" + addComment(input: AddCommentInput!): AddCommentPayload! + """Add or remove a set of label on a bug""" + changeLabels(input: ChangeLabelInput): ChangeLabelPayload! + """Change a bug's status to open""" + openBug(input: OpenBugInput!): OpenBugPayload! + """Change a bug's status to closed""" + closeBug(input: CloseBugInput!): CloseBugPayload! + """Change a bug's title""" + setTitle(input: SetTitleInput!): SetTitlePayload! + """Commit write the pending operations into storage. This mutation fail if nothing is pending""" + commit(input: CommitInput!): CommitPayload! + """Commit write the pending operations into storage. This mutation succed if nothing is pending""" + commitAsNeeded(input: CommitAsNeededInput!): CommitAsNeededPayload! +} +`, BuiltIn: false}, + &ast.Source{Name: "schema/timeline.graphql", Input: `"""An item in the timeline of events""" +interface TimelineItem { + """The identifier of the source operation""" + id: String! } -input OpenBugInput { - """ - A unique identifier for the client performing the mutation. - """ - clientMutationId: String - """ - "The name of the repository. If not set, the default repository is used. - """ - repoRef: String - """ - The bug ID's prefix. - """ - prefix: String! + +"""CommentHistoryStep hold one version of a message in the history""" +type CommentHistoryStep { + message: String! + date: Time! } -type OpenBugPayload { - """ - A unique identifier for the client performing the mutation. - """ - clientMutationId: String - """ - The affected bug. - """ - bug: Bug! - """ - The resulting operation. - """ - operation: SetStatusOperation! -} -""" -An operation applied to a bug. -""" -interface Operation { - """ - The identifier of the operation - """ - id: String! - """ - The operations author. - """ - author: Identity! - """ - The datetime when this operation was issued. - """ - date: Time! -} -""" -The connection type for an Operation -""" -type OperationConnection { - edges: [OperationEdge!]! - nodes: [Operation!]! - pageInfo: PageInfo! - totalCount: Int! -} -""" -Represent an Operation -""" -type OperationEdge { - cursor: String! - node: Operation! + +# Connection + +"""The connection type for TimelineItem""" +type TimelineItemConnection { + edges: [TimelineItemEdge!]! + nodes: [TimelineItem!]! + pageInfo: PageInfo! + totalCount: Int! } -""" -Information about pagination in a connection. -""" -type PageInfo { - """ - When paginating forwards, are there more items? - """ - hasNextPage: Boolean! - """ - When paginating backwards, are there more items? - """ - hasPreviousPage: Boolean! - """ - When paginating backwards, the cursor to continue. - """ - startCursor: String! - """ - When paginating forwards, the cursor to continue. - """ - endCursor: String! -} -type Query { - """ - The default unnamend repository. - """ - defaultRepository: Repository - """ - Access a repository by reference/name. - """ - repository(ref: String!): Repository + +"""Represent a TimelineItem""" +type TimelineItemEdge { + cursor: String! + node: TimelineItem! } -type Repository { - """ - All the bugs - """ - allBugs(""" - Returns the elements in the list that come after the specified cursor. - """ - after: String, """ - Returns the elements in the list that come before the specified cursor. - """ - before: String, """ - Returns the first _n_ elements from the list. - """ - first: Int, """ - Returns the last _n_ elements from the list. - """ - last: Int, """ - A query to select and order bugs - """ - query: String): BugConnection! - bug(prefix: String!): Bug - """ - All the identities - """ - allIdentities(""" - Returns the elements in the list that come after the specified cursor. - """ - after: String, """ - Returns the elements in the list that come before the specified cursor. - """ - before: String, """ - Returns the first _n_ elements from the list. - """ - first: Int, """ - Returns the last _n_ elements from the list. - """ - last: Int): IdentityConnection! - identity(prefix: String!): Identity - """ - The identity created or selected by the user as its own - """ - userIdentity: Identity - """ - List of valid labels. - """ - validLabels(""" - Returns the elements in the list that come after the specified cursor. - """ - after: String, """ - Returns the elements in the list that come before the specified cursor. - """ - before: String, """ - Returns the first _n_ elements from the list. - """ - first: Int, """ - Returns the last _n_ elements from the list. - """ - last: Int): LabelConnection! + +# Items + +"""CreateTimelineItem is a TimelineItem that represent the creation of a bug and its message edition history""" +type CreateTimelineItem implements TimelineItem & Authored { + """The identifier of the source operation""" + id: String! + author: Identity! + message: String! + messageIsEmpty: Boolean! + files: [Hash!]! + createdAt: Time! + lastEdit: Time! + edited: Boolean! + history: [CommentHistoryStep!]! +} + +"""AddCommentTimelineItem is a TimelineItem that represent a Comment and its edition history""" +type AddCommentTimelineItem implements TimelineItem & Authored { + """The identifier of the source operation""" + id: String! + author: Identity! + message: String! + messageIsEmpty: Boolean! + files: [Hash!]! + createdAt: Time! + lastEdit: Time! + edited: Boolean! + history: [CommentHistoryStep!]! +} + +"""LabelChangeTimelineItem is a TimelineItem that represent a change in the labels of a bug""" +type LabelChangeTimelineItem implements TimelineItem & Authored { + """The identifier of the source operation""" + id: String! + author: Identity! + date: Time! + added: [Label!]! + removed: [Label!]! } -type SetStatusOperation implements Operation & Authored { - """ - The identifier of the operation - """ - id: String! - """ - The author of this object. - """ - author: Identity! - """ - The datetime when this operation was issued. - """ - date: Time! - status: Status! -} -""" -SetStatusTimelineItem is a TimelineItem that represent a change in the status of a bug -""" + +"""SetStatusTimelineItem is a TimelineItem that represent a change in the status of a bug""" type SetStatusTimelineItem implements TimelineItem & Authored { - """ - The identifier of the source operation - """ - id: String! - author: Identity! - date: Time! - status: Status! -} -input SetTitleInput { - """ - A unique identifier for the client performing the mutation. - """ - clientMutationId: String - """ - "The name of the repository. If not set, the default repository is used. - """ - repoRef: String - """ - The bug ID's prefix. - """ - prefix: String! - """ - The new title. - """ - title: String! + """The identifier of the source operation""" + id: String! + author: Identity! + date: Time! + status: Status! } -type SetTitleOperation implements Operation & Authored { - """ - The identifier of the operation - """ - id: String! - """ - The author of this object. - """ - author: Identity! - """ - The datetime when this operation was issued. - """ - date: Time! - title: String! - was: String! -} -type SetTitlePayload { - """ - A unique identifier for the client performing the mutation. - """ - clientMutationId: String - """ - The affected bug. - """ - bug: Bug! - """ - The resulting operation - """ - operation: SetTitleOperation! -} -""" -LabelChangeTimelineItem is a TimelineItem that represent a change in the title of a bug -""" + +"""LabelChangeTimelineItem is a TimelineItem that represent a change in the title of a bug""" type SetTitleTimelineItem implements TimelineItem & Authored { - """ - The identifier of the source operation - """ - id: String! - author: Identity! - date: Time! - title: String! - was: String! + """The identifier of the source operation""" + id: String! + author: Identity! + date: Time! + title: String! + was: String! +} +`, BuiltIn: false}, + &ast.Source{Name: "schema/types.graphql", Input: `scalar Time +scalar Hash + +"""Defines a color by red, green and blue components.""" +type Color { + """Red component of the color.""" + R: Int! + """Green component of the color.""" + G: Int! + """Blue component of the color.""" + B: Int! } -enum Status { - OPEN - CLOSED + +"""Information about pagination in a connection.""" +type PageInfo { + """When paginating forwards, are there more items?""" + hasNextPage: Boolean! + """When paginating backwards, are there more items?""" + hasPreviousPage: Boolean! + """When paginating backwards, the cursor to continue.""" + startCursor: String! + """When paginating forwards, the cursor to continue.""" + endCursor: String! } -scalar Time -""" -An item in the timeline of events -""" -interface TimelineItem { - """ - The identifier of the source operation - """ - id: String! -} -""" -The connection type for TimelineItem -""" -type TimelineItemConnection { - edges: [TimelineItemEdge!]! - nodes: [TimelineItem!]! - pageInfo: PageInfo! - totalCount: Int! -} -""" -Represent a TimelineItem -""" -type TimelineItemEdge { - cursor: String! - node: TimelineItem! + +"""An object that has an author.""" +interface Authored { + """The author of this object.""" + author: Identity! } -`}, -) +`, BuiltIn: false}, +} +var parsedSchema = gqlparser.MustLoadSchema(sources...) // endregion ************************** generated!.gotpl ************************** @@ -3310,13 +3079,13 @@ func (ec *executionContext) _AddCommentOperation_author(ctx context.Context, fie Object: "AddCommentOperation", Field: field, Args: nil, - IsMethod: false, + IsMethod: true, } ctx = graphql.WithFieldContext(ctx, fc) resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Author, nil + return ec.resolvers.AddCommentOperation().Author(rctx, obj) }) if err != nil { ec.Error(ctx, err) @@ -3328,9 +3097,9 @@ func (ec *executionContext) _AddCommentOperation_author(ctx context.Context, fie } return graphql.Null } - res := resTmp.(identity.Interface) + res := resTmp.(models.IdentityWrapper) fc.Result = res - return ec.marshalNIdentity2githubáš—comáš‹MichaelMureáš‹gitáš‘bugáš‹identityášInterface(ctx, field.Selections, res) + return ec.marshalNIdentity2githubáš—comáš‹MichaelMureáš‹gitáš‘bugáš‹graphqláš‹modelsášIdentityWrapper(ctx, field.Selections, res) } func (ec *executionContext) _AddCommentOperation_date(ctx context.Context, field graphql.CollectedField, obj *bug.AddCommentOperation) (ret graphql.Marshaler) { @@ -3495,9 +3264,9 @@ func (ec *executionContext) _AddCommentPayload_bug(ctx context.Context, field gr } return graphql.Null } - res := resTmp.(*bug.Snapshot) + res := resTmp.(models.BugWrapper) fc.Result = res - return ec.marshalNBug2áš–githubáš—comáš‹MichaelMureáš‹gitáš‘bugáš‹bugášSnapshot(ctx, field.Selections, res) + return ec.marshalNBug2githubáš—comáš‹MichaelMureáš‹gitáš‘bugáš‹graphqláš‹modelsášBugWrapper(ctx, field.Selections, res) } func (ec *executionContext) _AddCommentPayload_operation(ctx context.Context, field graphql.CollectedField, obj *models.AddCommentPayload) (ret graphql.Marshaler) { @@ -3579,13 +3348,13 @@ func (ec *executionContext) _AddCommentTimelineItem_author(ctx context.Context, Object: "AddCommentTimelineItem", Field: field, Args: nil, - IsMethod: false, + IsMethod: true, } ctx = graphql.WithFieldContext(ctx, fc) resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Author, nil + return ec.resolvers.AddCommentTimelineItem().Author(rctx, obj) }) if err != nil { ec.Error(ctx, err) @@ -3597,9 +3366,9 @@ func (ec *executionContext) _AddCommentTimelineItem_author(ctx context.Context, } return graphql.Null } - res := resTmp.(identity.Interface) + res := resTmp.(models.IdentityWrapper) fc.Result = res - return ec.marshalNIdentity2githubáš—comáš‹MichaelMureáš‹gitáš‘bugáš‹identityášInterface(ctx, field.Selections, res) + return ec.marshalNIdentity2githubáš—comáš‹MichaelMureáš‹gitáš‘bugáš‹graphqláš‹modelsášIdentityWrapper(ctx, field.Selections, res) } func (ec *executionContext) _AddCommentTimelineItem_message(ctx context.Context, field graphql.CollectedField, obj *bug.AddCommentTimelineItem) (ret graphql.Marshaler) { @@ -3840,7 +3609,7 @@ func (ec *executionContext) _AddCommentTimelineItem_history(ctx context.Context, return ec.marshalNCommentHistoryStep2áš•githubáš—comáš‹MichaelMureáš‹gitáš‘bugáš‹bugášCommentHistoryStepáš„(ctx, field.Selections, res) } -func (ec *executionContext) _Bug_id(ctx context.Context, field graphql.CollectedField, obj *bug.Snapshot) (ret graphql.Marshaler) { +func (ec *executionContext) _Bug_id(ctx context.Context, field graphql.CollectedField, obj models.BugWrapper) (ret graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -3874,7 +3643,7 @@ func (ec *executionContext) _Bug_id(ctx context.Context, field graphql.Collected return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) _Bug_humanId(ctx context.Context, field graphql.CollectedField, obj *bug.Snapshot) (ret graphql.Marshaler) { +func (ec *executionContext) _Bug_humanId(ctx context.Context, field graphql.CollectedField, obj models.BugWrapper) (ret graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -3908,7 +3677,7 @@ func (ec *executionContext) _Bug_humanId(ctx context.Context, field graphql.Coll return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) _Bug_status(ctx context.Context, field graphql.CollectedField, obj *bug.Snapshot) (ret graphql.Marshaler) { +func (ec *executionContext) _Bug_status(ctx context.Context, field graphql.CollectedField, obj models.BugWrapper) (ret graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -3942,7 +3711,7 @@ func (ec *executionContext) _Bug_status(ctx context.Context, field graphql.Colle return ec.marshalNStatus2githubáš—comáš‹MichaelMureáš‹gitáš‘bugáš‹graphqláš‹modelsášStatus(ctx, field.Selections, res) } -func (ec *executionContext) _Bug_title(ctx context.Context, field graphql.CollectedField, obj *bug.Snapshot) (ret graphql.Marshaler) { +func (ec *executionContext) _Bug_title(ctx context.Context, field graphql.CollectedField, obj models.BugWrapper) (ret graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -3953,13 +3722,13 @@ func (ec *executionContext) _Bug_title(ctx context.Context, field graphql.Collec Object: "Bug", Field: field, Args: nil, - IsMethod: false, + IsMethod: true, } ctx = graphql.WithFieldContext(ctx, fc) resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Title, nil + return obj.Title(), nil }) if err != nil { ec.Error(ctx, err) @@ -3976,7 +3745,7 @@ func (ec *executionContext) _Bug_title(ctx context.Context, field graphql.Collec return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) _Bug_labels(ctx context.Context, field graphql.CollectedField, obj *bug.Snapshot) (ret graphql.Marshaler) { +func (ec *executionContext) _Bug_labels(ctx context.Context, field graphql.CollectedField, obj models.BugWrapper) (ret graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -3987,13 +3756,13 @@ func (ec *executionContext) _Bug_labels(ctx context.Context, field graphql.Colle Object: "Bug", Field: field, Args: nil, - IsMethod: false, + IsMethod: true, } ctx = graphql.WithFieldContext(ctx, fc) resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Labels, nil + return obj.Labels(), nil }) if err != nil { ec.Error(ctx, err) @@ -4010,7 +3779,7 @@ func (ec *executionContext) _Bug_labels(ctx context.Context, field graphql.Colle return ec.marshalNLabel2áš•githubáš—comáš‹MichaelMureáš‹gitáš‘bugáš‹bugášLabeláš„(ctx, field.Selections, res) } -func (ec *executionContext) _Bug_author(ctx context.Context, field graphql.CollectedField, obj *bug.Snapshot) (ret graphql.Marshaler) { +func (ec *executionContext) _Bug_author(ctx context.Context, field graphql.CollectedField, obj models.BugWrapper) (ret graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -4021,13 +3790,13 @@ func (ec *executionContext) _Bug_author(ctx context.Context, field graphql.Colle Object: "Bug", Field: field, Args: nil, - IsMethod: false, + IsMethod: true, } ctx = graphql.WithFieldContext(ctx, fc) resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Author, nil + return obj.Author() }) if err != nil { ec.Error(ctx, err) @@ -4039,12 +3808,12 @@ func (ec *executionContext) _Bug_author(ctx context.Context, field graphql.Colle } return graphql.Null } - res := resTmp.(identity.Interface) + res := resTmp.(models.IdentityWrapper) fc.Result = res - return ec.marshalNIdentity2githubáš—comáš‹MichaelMureáš‹gitáš‘bugáš‹identityášInterface(ctx, field.Selections, res) + return ec.marshalNIdentity2githubáš—comáš‹MichaelMureáš‹gitáš‘bugáš‹graphqláš‹modelsášIdentityWrapper(ctx, field.Selections, res) } -func (ec *executionContext) _Bug_createdAt(ctx context.Context, field graphql.CollectedField, obj *bug.Snapshot) (ret graphql.Marshaler) { +func (ec *executionContext) _Bug_createdAt(ctx context.Context, field graphql.CollectedField, obj models.BugWrapper) (ret graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -4055,13 +3824,13 @@ func (ec *executionContext) _Bug_createdAt(ctx context.Context, field graphql.Co Object: "Bug", Field: field, Args: nil, - IsMethod: false, + IsMethod: true, } ctx = graphql.WithFieldContext(ctx, fc) resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.CreatedAt, nil + return obj.CreatedAt(), nil }) if err != nil { ec.Error(ctx, err) @@ -4078,7 +3847,7 @@ func (ec *executionContext) _Bug_createdAt(ctx context.Context, field graphql.Co return ec.marshalNTime2timeášTime(ctx, field.Selections, res) } -func (ec *executionContext) _Bug_lastEdit(ctx context.Context, field graphql.CollectedField, obj *bug.Snapshot) (ret graphql.Marshaler) { +func (ec *executionContext) _Bug_lastEdit(ctx context.Context, field graphql.CollectedField, obj models.BugWrapper) (ret graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -4095,7 +3864,7 @@ func (ec *executionContext) _Bug_lastEdit(ctx context.Context, field graphql.Col ctx = graphql.WithFieldContext(ctx, fc) resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.Bug().LastEdit(rctx, obj) + return obj.LastEdit(), nil }) if err != nil { ec.Error(ctx, err) @@ -4107,12 +3876,12 @@ func (ec *executionContext) _Bug_lastEdit(ctx context.Context, field graphql.Col } return graphql.Null } - res := resTmp.(*time.Time) + res := resTmp.(time.Time) fc.Result = res - return ec.marshalNTime2áš–timeášTime(ctx, field.Selections, res) + return ec.marshalNTime2timeášTime(ctx, field.Selections, res) } -func (ec *executionContext) _Bug_actors(ctx context.Context, field graphql.CollectedField, obj *bug.Snapshot) (ret graphql.Marshaler) { +func (ec *executionContext) _Bug_actors(ctx context.Context, field graphql.CollectedField, obj models.BugWrapper) (ret graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -4153,7 +3922,7 @@ func (ec *executionContext) _Bug_actors(ctx context.Context, field graphql.Colle return ec.marshalNIdentityConnection2áš–githubáš—comáš‹MichaelMureáš‹gitáš‘bugáš‹graphqláš‹modelsášIdentityConnection(ctx, field.Selections, res) } -func (ec *executionContext) _Bug_participants(ctx context.Context, field graphql.CollectedField, obj *bug.Snapshot) (ret graphql.Marshaler) { +func (ec *executionContext) _Bug_participants(ctx context.Context, field graphql.CollectedField, obj models.BugWrapper) (ret graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -4194,7 +3963,7 @@ func (ec *executionContext) _Bug_participants(ctx context.Context, field graphql return ec.marshalNIdentityConnection2áš–githubáš—comáš‹MichaelMureáš‹gitáš‘bugáš‹graphqláš‹modelsášIdentityConnection(ctx, field.Selections, res) } -func (ec *executionContext) _Bug_comments(ctx context.Context, field graphql.CollectedField, obj *bug.Snapshot) (ret graphql.Marshaler) { +func (ec *executionContext) _Bug_comments(ctx context.Context, field graphql.CollectedField, obj models.BugWrapper) (ret graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -4235,7 +4004,7 @@ func (ec *executionContext) _Bug_comments(ctx context.Context, field graphql.Col return ec.marshalNCommentConnection2áš–githubáš—comáš‹MichaelMureáš‹gitáš‘bugáš‹graphqláš‹modelsášCommentConnection(ctx, field.Selections, res) } -func (ec *executionContext) _Bug_timeline(ctx context.Context, field graphql.CollectedField, obj *bug.Snapshot) (ret graphql.Marshaler) { +func (ec *executionContext) _Bug_timeline(ctx context.Context, field graphql.CollectedField, obj models.BugWrapper) (ret graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -4276,7 +4045,7 @@ func (ec *executionContext) _Bug_timeline(ctx context.Context, field graphql.Col return ec.marshalNTimelineItemConnection2áš–githubáš—comáš‹MichaelMureáš‹gitáš‘bugáš‹graphqláš‹modelsášTimelineItemConnection(ctx, field.Selections, res) } -func (ec *executionContext) _Bug_operations(ctx context.Context, field graphql.CollectedField, obj *bug.Snapshot) (ret graphql.Marshaler) { +func (ec *executionContext) _Bug_operations(ctx context.Context, field graphql.CollectedField, obj models.BugWrapper) (ret graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -4380,9 +4149,9 @@ func (ec *executionContext) _BugConnection_nodes(ctx context.Context, field grap } return graphql.Null } - res := resTmp.([]*bug.Snapshot) + res := resTmp.([]models.BugWrapper) fc.Result = res - return ec.marshalNBug2áš•áš–githubáš—comáš‹MichaelMureáš‹gitáš‘bugáš‹bugášSnapshotáš„(ctx, field.Selections, res) + return ec.marshalNBug2áš•githubáš—comáš‹MichaelMureáš‹gitáš‘bugáš‹graphqláš‹modelsášBugWrapperáš„(ctx, field.Selections, res) } func (ec *executionContext) _BugConnection_pageInfo(ctx context.Context, field graphql.CollectedField, obj *models.BugConnection) (ret graphql.Marshaler) { @@ -4516,9 +4285,9 @@ func (ec *executionContext) _BugEdge_node(ctx context.Context, field graphql.Col } return graphql.Null } - res := resTmp.(*bug.Snapshot) + res := resTmp.(models.BugWrapper) fc.Result = res - return ec.marshalNBug2áš–githubáš—comáš‹MichaelMureáš‹gitáš‘bugáš‹bugášSnapshot(ctx, field.Selections, res) + return ec.marshalNBug2githubáš—comáš‹MichaelMureáš‹gitáš‘bugáš‹graphqláš‹modelsášBugWrapper(ctx, field.Selections, res) } func (ec *executionContext) _ChangeLabelPayload_clientMutationId(ctx context.Context, field graphql.CollectedField, obj *models.ChangeLabelPayload) (ret graphql.Marshaler) { @@ -4581,9 +4350,9 @@ func (ec *executionContext) _ChangeLabelPayload_bug(ctx context.Context, field g } return graphql.Null } - res := resTmp.(*bug.Snapshot) + res := resTmp.(models.BugWrapper) fc.Result = res - return ec.marshalNBug2áš–githubáš—comáš‹MichaelMureáš‹gitáš‘bugáš‹bugášSnapshot(ctx, field.Selections, res) + return ec.marshalNBug2githubáš—comáš‹MichaelMureáš‹gitáš‘bugáš‹graphqláš‹modelsášBugWrapper(ctx, field.Selections, res) } func (ec *executionContext) _ChangeLabelPayload_operation(ctx context.Context, field graphql.CollectedField, obj *models.ChangeLabelPayload) (ret graphql.Marshaler) { @@ -4714,9 +4483,9 @@ func (ec *executionContext) _CloseBugPayload_bug(ctx context.Context, field grap } return graphql.Null } - res := resTmp.(*bug.Snapshot) + res := resTmp.(models.BugWrapper) fc.Result = res - return ec.marshalNBug2áš–githubáš—comáš‹MichaelMureáš‹gitáš‘bugáš‹bugášSnapshot(ctx, field.Selections, res) + return ec.marshalNBug2githubáš—comáš‹MichaelMureáš‹gitáš‘bugáš‹graphqláš‹modelsášBugWrapper(ctx, field.Selections, res) } func (ec *executionContext) _CloseBugPayload_operation(ctx context.Context, field graphql.CollectedField, obj *models.CloseBugPayload) (ret graphql.Marshaler) { @@ -4866,13 +4635,13 @@ func (ec *executionContext) _Comment_author(ctx context.Context, field graphql.C Object: "Comment", Field: field, Args: nil, - IsMethod: false, + IsMethod: true, } ctx = graphql.WithFieldContext(ctx, fc) resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Author, nil + return ec.resolvers.Comment().Author(rctx, obj) }) if err != nil { ec.Error(ctx, err) @@ -4884,9 +4653,9 @@ func (ec *executionContext) _Comment_author(ctx context.Context, field graphql.C } return graphql.Null } - res := resTmp.(identity.Interface) + res := resTmp.(models.IdentityWrapper) fc.Result = res - return ec.marshalNIdentity2githubáš—comáš‹MichaelMureáš‹gitáš‘bugáš‹identityášInterface(ctx, field.Selections, res) + return ec.marshalNIdentity2githubáš—comáš‹MichaelMureáš‹gitáš‘bugáš‹graphqláš‹modelsášIdentityWrapper(ctx, field.Selections, res) } func (ec *executionContext) _Comment_message(ctx context.Context, field graphql.CollectedField, obj *bug.Comment) (ret graphql.Marshaler) { @@ -5289,9 +5058,9 @@ func (ec *executionContext) _CommitAsNeededPayload_bug(ctx context.Context, fiel } return graphql.Null } - res := resTmp.(*bug.Snapshot) + res := resTmp.(models.BugWrapper) fc.Result = res - return ec.marshalNBug2áš–githubáš—comáš‹MichaelMureáš‹gitáš‘bugáš‹bugášSnapshot(ctx, field.Selections, res) + return ec.marshalNBug2githubáš—comáš‹MichaelMureáš‹gitáš‘bugáš‹graphqláš‹modelsášBugWrapper(ctx, field.Selections, res) } func (ec *executionContext) _CommitPayload_clientMutationId(ctx context.Context, field graphql.CollectedField, obj *models.CommitPayload) (ret graphql.Marshaler) { @@ -5354,9 +5123,9 @@ func (ec *executionContext) _CommitPayload_bug(ctx context.Context, field graphq } return graphql.Null } - res := resTmp.(*bug.Snapshot) + res := resTmp.(models.BugWrapper) fc.Result = res - return ec.marshalNBug2áš–githubáš—comáš‹MichaelMureáš‹gitáš‘bugáš‹bugášSnapshot(ctx, field.Selections, res) + return ec.marshalNBug2githubáš—comáš‹MichaelMureáš‹gitáš‘bugáš‹graphqláš‹modelsášBugWrapper(ctx, field.Selections, res) } func (ec *executionContext) _CreateOperation_id(ctx context.Context, field graphql.CollectedField, obj *bug.CreateOperation) (ret graphql.Marshaler) { @@ -5404,13 +5173,13 @@ func (ec *executionContext) _CreateOperation_author(ctx context.Context, field g Object: "CreateOperation", Field: field, Args: nil, - IsMethod: false, + IsMethod: true, } ctx = graphql.WithFieldContext(ctx, fc) resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Author, nil + return ec.resolvers.CreateOperation().Author(rctx, obj) }) if err != nil { ec.Error(ctx, err) @@ -5422,9 +5191,9 @@ func (ec *executionContext) _CreateOperation_author(ctx context.Context, field g } return graphql.Null } - res := resTmp.(identity.Interface) + res := resTmp.(models.IdentityWrapper) fc.Result = res - return ec.marshalNIdentity2githubáš—comáš‹MichaelMureáš‹gitáš‘bugáš‹identityášInterface(ctx, field.Selections, res) + return ec.marshalNIdentity2githubáš—comáš‹MichaelMureáš‹gitáš‘bugáš‹graphqláš‹modelsášIdentityWrapper(ctx, field.Selections, res) } func (ec *executionContext) _CreateOperation_date(ctx context.Context, field graphql.CollectedField, obj *bug.CreateOperation) (ret graphql.Marshaler) { @@ -5608,13 +5377,13 @@ func (ec *executionContext) _CreateTimelineItem_author(ctx context.Context, fiel Object: "CreateTimelineItem", Field: field, Args: nil, - IsMethod: false, + IsMethod: true, } ctx = graphql.WithFieldContext(ctx, fc) resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Author, nil + return ec.resolvers.CreateTimelineItem().Author(rctx, obj) }) if err != nil { ec.Error(ctx, err) @@ -5626,9 +5395,9 @@ func (ec *executionContext) _CreateTimelineItem_author(ctx context.Context, fiel } return graphql.Null } - res := resTmp.(identity.Interface) + res := resTmp.(models.IdentityWrapper) fc.Result = res - return ec.marshalNIdentity2githubáš—comáš‹MichaelMureáš‹gitáš‘bugáš‹identityášInterface(ctx, field.Selections, res) + return ec.marshalNIdentity2githubáš—comáš‹MichaelMureáš‹gitáš‘bugáš‹graphqláš‹modelsášIdentityWrapper(ctx, field.Selections, res) } func (ec *executionContext) _CreateTimelineItem_message(ctx context.Context, field graphql.CollectedField, obj *bug.CreateTimelineItem) (ret graphql.Marshaler) { @@ -5914,13 +5683,13 @@ func (ec *executionContext) _EditCommentOperation_author(ctx context.Context, fi Object: "EditCommentOperation", Field: field, Args: nil, - IsMethod: false, + IsMethod: true, } ctx = graphql.WithFieldContext(ctx, fc) resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Author, nil + return ec.resolvers.EditCommentOperation().Author(rctx, obj) }) if err != nil { ec.Error(ctx, err) @@ -5932,9 +5701,9 @@ func (ec *executionContext) _EditCommentOperation_author(ctx context.Context, fi } return graphql.Null } - res := resTmp.(identity.Interface) + res := resTmp.(models.IdentityWrapper) fc.Result = res - return ec.marshalNIdentity2githubáš—comáš‹MichaelMureáš‹gitáš‘bugáš‹identityášInterface(ctx, field.Selections, res) + return ec.marshalNIdentity2githubáš—comáš‹MichaelMureáš‹gitáš‘bugáš‹graphqláš‹modelsášIdentityWrapper(ctx, field.Selections, res) } func (ec *executionContext) _EditCommentOperation_date(ctx context.Context, field graphql.CollectedField, obj *bug.EditCommentOperation) (ret graphql.Marshaler) { @@ -6073,7 +5842,7 @@ func (ec *executionContext) _EditCommentOperation_files(ctx context.Context, fie return ec.marshalNHash2áš•githubáš—comáš‹MichaelMureáš‹gitáš‘bugáš‹utiláš‹gitášHasháš„(ctx, field.Selections, res) } -func (ec *executionContext) _Identity_id(ctx context.Context, field graphql.CollectedField, obj identity.Interface) (ret graphql.Marshaler) { +func (ec *executionContext) _Identity_id(ctx context.Context, field graphql.CollectedField, obj models.IdentityWrapper) (ret graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -6107,7 +5876,7 @@ func (ec *executionContext) _Identity_id(ctx context.Context, field graphql.Coll return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) _Identity_humanId(ctx context.Context, field graphql.CollectedField, obj identity.Interface) (ret graphql.Marshaler) { +func (ec *executionContext) _Identity_humanId(ctx context.Context, field graphql.CollectedField, obj models.IdentityWrapper) (ret graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -6141,7 +5910,7 @@ func (ec *executionContext) _Identity_humanId(ctx context.Context, field graphql return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) _Identity_name(ctx context.Context, field graphql.CollectedField, obj identity.Interface) (ret graphql.Marshaler) { +func (ec *executionContext) _Identity_name(ctx context.Context, field graphql.CollectedField, obj models.IdentityWrapper) (ret graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -6172,7 +5941,7 @@ func (ec *executionContext) _Identity_name(ctx context.Context, field graphql.Co return ec.marshalOString2string(ctx, field.Selections, res) } -func (ec *executionContext) _Identity_email(ctx context.Context, field graphql.CollectedField, obj identity.Interface) (ret graphql.Marshaler) { +func (ec *executionContext) _Identity_email(ctx context.Context, field graphql.CollectedField, obj models.IdentityWrapper) (ret graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -6189,7 +5958,7 @@ func (ec *executionContext) _Identity_email(ctx context.Context, field graphql.C ctx = graphql.WithFieldContext(ctx, fc) resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Email(), nil + return obj.Email() }) if err != nil { ec.Error(ctx, err) @@ -6203,7 +5972,7 @@ func (ec *executionContext) _Identity_email(ctx context.Context, field graphql.C return ec.marshalOString2string(ctx, field.Selections, res) } -func (ec *executionContext) _Identity_displayName(ctx context.Context, field graphql.CollectedField, obj identity.Interface) (ret graphql.Marshaler) { +func (ec *executionContext) _Identity_displayName(ctx context.Context, field graphql.CollectedField, obj models.IdentityWrapper) (ret graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -6237,7 +6006,7 @@ func (ec *executionContext) _Identity_displayName(ctx context.Context, field gra return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) _Identity_avatarUrl(ctx context.Context, field graphql.CollectedField, obj identity.Interface) (ret graphql.Marshaler) { +func (ec *executionContext) _Identity_avatarUrl(ctx context.Context, field graphql.CollectedField, obj models.IdentityWrapper) (ret graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -6254,7 +6023,7 @@ func (ec *executionContext) _Identity_avatarUrl(ctx context.Context, field graph ctx = graphql.WithFieldContext(ctx, fc) resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.AvatarUrl(), nil + return obj.AvatarUrl() }) if err != nil { ec.Error(ctx, err) @@ -6268,7 +6037,7 @@ func (ec *executionContext) _Identity_avatarUrl(ctx context.Context, field graph return ec.marshalOString2string(ctx, field.Selections, res) } -func (ec *executionContext) _Identity_isProtected(ctx context.Context, field graphql.CollectedField, obj identity.Interface) (ret graphql.Marshaler) { +func (ec *executionContext) _Identity_isProtected(ctx context.Context, field graphql.CollectedField, obj models.IdentityWrapper) (ret graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -6285,7 +6054,7 @@ func (ec *executionContext) _Identity_isProtected(ctx context.Context, field gra ctx = graphql.WithFieldContext(ctx, fc) resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.IsProtected(), nil + return obj.IsProtected() }) if err != nil { ec.Error(ctx, err) @@ -6365,9 +6134,9 @@ func (ec *executionContext) _IdentityConnection_nodes(ctx context.Context, field } return graphql.Null } - res := resTmp.([]identity.Interface) + res := resTmp.([]models.IdentityWrapper) fc.Result = res - return ec.marshalNIdentity2áš•githubáš—comáš‹MichaelMureáš‹gitáš‘bugáš‹identityášInterfaceáš„(ctx, field.Selections, res) + return ec.marshalNIdentity2áš•githubáš—comáš‹MichaelMureáš‹gitáš‘bugáš‹graphqláš‹modelsášIdentityWrapperáš„(ctx, field.Selections, res) } func (ec *executionContext) _IdentityConnection_pageInfo(ctx context.Context, field graphql.CollectedField, obj *models.IdentityConnection) (ret graphql.Marshaler) { @@ -6501,9 +6270,9 @@ func (ec *executionContext) _IdentityEdge_node(ctx context.Context, field graphq } return graphql.Null } - res := resTmp.(identity.Interface) + res := resTmp.(models.IdentityWrapper) fc.Result = res - return ec.marshalNIdentity2githubáš—comáš‹MichaelMureáš‹gitáš‘bugáš‹identityášInterface(ctx, field.Selections, res) + return ec.marshalNIdentity2githubáš—comáš‹MichaelMureáš‹gitáš‘bugáš‹graphqláš‹modelsášIdentityWrapper(ctx, field.Selections, res) } func (ec *executionContext) _Label_name(ctx context.Context, field graphql.CollectedField, obj *bug.Label) (ret graphql.Marshaler) { @@ -6619,13 +6388,13 @@ func (ec *executionContext) _LabelChangeOperation_author(ctx context.Context, fi Object: "LabelChangeOperation", Field: field, Args: nil, - IsMethod: false, + IsMethod: true, } ctx = graphql.WithFieldContext(ctx, fc) resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Author, nil + return ec.resolvers.LabelChangeOperation().Author(rctx, obj) }) if err != nil { ec.Error(ctx, err) @@ -6637,9 +6406,9 @@ func (ec *executionContext) _LabelChangeOperation_author(ctx context.Context, fi } return graphql.Null } - res := resTmp.(identity.Interface) + res := resTmp.(models.IdentityWrapper) fc.Result = res - return ec.marshalNIdentity2githubáš—comáš‹MichaelMureáš‹gitáš‘bugáš‹identityášInterface(ctx, field.Selections, res) + return ec.marshalNIdentity2githubáš—comáš‹MichaelMureáš‹gitáš‘bugáš‹graphqláš‹modelsášIdentityWrapper(ctx, field.Selections, res) } func (ec *executionContext) _LabelChangeOperation_date(ctx context.Context, field graphql.CollectedField, obj *bug.LabelChangeOperation) (ret graphql.Marshaler) { @@ -6857,13 +6626,13 @@ func (ec *executionContext) _LabelChangeTimelineItem_author(ctx context.Context, Object: "LabelChangeTimelineItem", Field: field, Args: nil, - IsMethod: false, + IsMethod: true, } ctx = graphql.WithFieldContext(ctx, fc) resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Author, nil + return ec.resolvers.LabelChangeTimelineItem().Author(rctx, obj) }) if err != nil { ec.Error(ctx, err) @@ -6875,9 +6644,9 @@ func (ec *executionContext) _LabelChangeTimelineItem_author(ctx context.Context, } return graphql.Null } - res := resTmp.(identity.Interface) + res := resTmp.(models.IdentityWrapper) fc.Result = res - return ec.marshalNIdentity2githubáš—comáš‹MichaelMureáš‹gitáš‘bugáš‹identityášInterface(ctx, field.Selections, res) + return ec.marshalNIdentity2githubáš—comáš‹MichaelMureáš‹gitáš‘bugáš‹graphqláš‹modelsášIdentityWrapper(ctx, field.Selections, res) } func (ec *executionContext) _LabelChangeTimelineItem_date(ctx context.Context, field graphql.CollectedField, obj *bug.LabelChangeTimelineItem) (ret graphql.Marshaler) { @@ -7574,9 +7343,9 @@ func (ec *executionContext) _NewBugPayload_bug(ctx context.Context, field graphq } return graphql.Null } - res := resTmp.(*bug.Snapshot) + res := resTmp.(models.BugWrapper) fc.Result = res - return ec.marshalNBug2áš–githubáš—comáš‹MichaelMureáš‹gitáš‘bugáš‹bugášSnapshot(ctx, field.Selections, res) + return ec.marshalNBug2githubáš—comáš‹MichaelMureáš‹gitáš‘bugáš‹graphqláš‹modelsášBugWrapper(ctx, field.Selections, res) } func (ec *executionContext) _NewBugPayload_operation(ctx context.Context, field graphql.CollectedField, obj *models.NewBugPayload) (ret graphql.Marshaler) { @@ -7673,9 +7442,9 @@ func (ec *executionContext) _OpenBugPayload_bug(ctx context.Context, field graph } return graphql.Null } - res := resTmp.(*bug.Snapshot) + res := resTmp.(models.BugWrapper) fc.Result = res - return ec.marshalNBug2áš–githubáš—comáš‹MichaelMureáš‹gitáš‘bugáš‹bugášSnapshot(ctx, field.Selections, res) + return ec.marshalNBug2githubáš—comáš‹MichaelMureáš‹gitáš‘bugáš‹graphqláš‹modelsášBugWrapper(ctx, field.Selections, res) } func (ec *executionContext) _OpenBugPayload_operation(ctx context.Context, field graphql.CollectedField, obj *models.OpenBugPayload) (ret graphql.Marshaler) { @@ -8264,9 +8033,9 @@ func (ec *executionContext) _Repository_bug(ctx context.Context, field graphql.C if resTmp == nil { return graphql.Null } - res := resTmp.(*bug.Snapshot) + res := resTmp.(models.BugWrapper) fc.Result = res - return ec.marshalOBug2áš–githubáš—comáš‹MichaelMureáš‹gitáš‘bugáš‹bugášSnapshot(ctx, field.Selections, res) + return ec.marshalOBug2githubáš—comáš‹MichaelMureáš‹gitáš‘bugáš‹graphqláš‹modelsášBugWrapper(ctx, field.Selections, res) } func (ec *executionContext) _Repository_allIdentities(ctx context.Context, field graphql.CollectedField, obj *models.Repository) (ret graphql.Marshaler) { @@ -8343,9 +8112,9 @@ func (ec *executionContext) _Repository_identity(ctx context.Context, field grap if resTmp == nil { return graphql.Null } - res := resTmp.(identity.Interface) + res := resTmp.(models.IdentityWrapper) fc.Result = res - return ec.marshalOIdentity2githubáš—comáš‹MichaelMureáš‹gitáš‘bugáš‹identityášInterface(ctx, field.Selections, res) + return ec.marshalOIdentity2githubáš—comáš‹MichaelMureáš‹gitáš‘bugáš‹graphqláš‹modelsášIdentityWrapper(ctx, field.Selections, res) } func (ec *executionContext) _Repository_userIdentity(ctx context.Context, field graphql.CollectedField, obj *models.Repository) (ret graphql.Marshaler) { @@ -8374,9 +8143,9 @@ func (ec *executionContext) _Repository_userIdentity(ctx context.Context, field if resTmp == nil { return graphql.Null } - res := resTmp.(identity.Interface) + res := resTmp.(models.IdentityWrapper) fc.Result = res - return ec.marshalOIdentity2githubáš—comáš‹MichaelMureáš‹gitáš‘bugáš‹identityášInterface(ctx, field.Selections, res) + return ec.marshalOIdentity2githubáš—comáš‹MichaelMureáš‹gitáš‘bugáš‹graphqláš‹modelsášIdentityWrapper(ctx, field.Selections, res) } func (ec *executionContext) _Repository_validLabels(ctx context.Context, field graphql.CollectedField, obj *models.Repository) (ret graphql.Marshaler) { @@ -8465,13 +8234,13 @@ func (ec *executionContext) _SetStatusOperation_author(ctx context.Context, fiel Object: "SetStatusOperation", Field: field, Args: nil, - IsMethod: false, + IsMethod: true, } ctx = graphql.WithFieldContext(ctx, fc) resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Author, nil + return ec.resolvers.SetStatusOperation().Author(rctx, obj) }) if err != nil { ec.Error(ctx, err) @@ -8483,9 +8252,9 @@ func (ec *executionContext) _SetStatusOperation_author(ctx context.Context, fiel } return graphql.Null } - res := resTmp.(identity.Interface) + res := resTmp.(models.IdentityWrapper) fc.Result = res - return ec.marshalNIdentity2githubáš—comáš‹MichaelMureáš‹gitáš‘bugáš‹identityášInterface(ctx, field.Selections, res) + return ec.marshalNIdentity2githubáš—comáš‹MichaelMureáš‹gitáš‘bugáš‹graphqláš‹modelsášIdentityWrapper(ctx, field.Selections, res) } func (ec *executionContext) _SetStatusOperation_date(ctx context.Context, field graphql.CollectedField, obj *bug.SetStatusOperation) (ret graphql.Marshaler) { @@ -8601,13 +8370,13 @@ func (ec *executionContext) _SetStatusTimelineItem_author(ctx context.Context, f Object: "SetStatusTimelineItem", Field: field, Args: nil, - IsMethod: false, + IsMethod: true, } ctx = graphql.WithFieldContext(ctx, fc) resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Author, nil + return ec.resolvers.SetStatusTimelineItem().Author(rctx, obj) }) if err != nil { ec.Error(ctx, err) @@ -8619,9 +8388,9 @@ func (ec *executionContext) _SetStatusTimelineItem_author(ctx context.Context, f } return graphql.Null } - res := resTmp.(identity.Interface) + res := resTmp.(models.IdentityWrapper) fc.Result = res - return ec.marshalNIdentity2githubáš—comáš‹MichaelMureáš‹gitáš‘bugáš‹identityášInterface(ctx, field.Selections, res) + return ec.marshalNIdentity2githubáš—comáš‹MichaelMureáš‹gitáš‘bugáš‹graphqláš‹modelsášIdentityWrapper(ctx, field.Selections, res) } func (ec *executionContext) _SetStatusTimelineItem_date(ctx context.Context, field graphql.CollectedField, obj *bug.SetStatusTimelineItem) (ret graphql.Marshaler) { @@ -8737,13 +8506,13 @@ func (ec *executionContext) _SetTitleOperation_author(ctx context.Context, field Object: "SetTitleOperation", Field: field, Args: nil, - IsMethod: false, + IsMethod: true, } ctx = graphql.WithFieldContext(ctx, fc) resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Author, nil + return ec.resolvers.SetTitleOperation().Author(rctx, obj) }) if err != nil { ec.Error(ctx, err) @@ -8755,9 +8524,9 @@ func (ec *executionContext) _SetTitleOperation_author(ctx context.Context, field } return graphql.Null } - res := resTmp.(identity.Interface) + res := resTmp.(models.IdentityWrapper) fc.Result = res - return ec.marshalNIdentity2githubáš—comáš‹MichaelMureáš‹gitáš‘bugáš‹identityášInterface(ctx, field.Selections, res) + return ec.marshalNIdentity2githubáš—comáš‹MichaelMureáš‹gitáš‘bugáš‹graphqláš‹modelsášIdentityWrapper(ctx, field.Selections, res) } func (ec *executionContext) _SetTitleOperation_date(ctx context.Context, field graphql.CollectedField, obj *bug.SetTitleOperation) (ret graphql.Marshaler) { @@ -8922,9 +8691,9 @@ func (ec *executionContext) _SetTitlePayload_bug(ctx context.Context, field grap } return graphql.Null } - res := resTmp.(*bug.Snapshot) + res := resTmp.(models.BugWrapper) fc.Result = res - return ec.marshalNBug2áš–githubáš—comáš‹MichaelMureáš‹gitáš‘bugáš‹bugášSnapshot(ctx, field.Selections, res) + return ec.marshalNBug2githubáš—comáš‹MichaelMureáš‹gitáš‘bugáš‹graphqláš‹modelsášBugWrapper(ctx, field.Selections, res) } func (ec *executionContext) _SetTitlePayload_operation(ctx context.Context, field graphql.CollectedField, obj *models.SetTitlePayload) (ret graphql.Marshaler) { @@ -9006,13 +8775,13 @@ func (ec *executionContext) _SetTitleTimelineItem_author(ctx context.Context, fi Object: "SetTitleTimelineItem", Field: field, Args: nil, - IsMethod: false, + IsMethod: true, } ctx = graphql.WithFieldContext(ctx, fc) resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Author, nil + return ec.resolvers.SetTitleTimelineItem().Author(rctx, obj) }) if err != nil { ec.Error(ctx, err) @@ -9024,9 +8793,9 @@ func (ec *executionContext) _SetTitleTimelineItem_author(ctx context.Context, fi } return graphql.Null } - res := resTmp.(identity.Interface) + res := resTmp.(models.IdentityWrapper) fc.Result = res - return ec.marshalNIdentity2githubáš—comáš‹MichaelMureáš‹gitáš‘bugáš‹identityášInterface(ctx, field.Selections, res) + return ec.marshalNIdentity2githubáš—comáš‹MichaelMureáš‹gitáš‘bugáš‹graphqláš‹modelsášIdentityWrapper(ctx, field.Selections, res) } func (ec *executionContext) _SetTitleTimelineItem_date(ctx context.Context, field graphql.CollectedField, obj *bug.SetTitleTimelineItem) (ret graphql.Marshaler) { @@ -10687,7 +10456,7 @@ func (ec *executionContext) _Authored(ctx context.Context, sel ast.SelectionSet, return graphql.Null } return ec._Comment(ctx, sel, obj) - case *bug.Snapshot: + case models.BugWrapper: if obj == nil { return graphql.Null } @@ -10861,10 +10630,19 @@ func (ec *executionContext) _AddCommentOperation(ctx context.Context, sel ast.Se return res }) case "author": - out.Values[i] = ec._AddCommentOperation_author(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_author(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&invalids, 1) + } + return res + }) case "date": field := field out.Concurrently(i, func() (res graphql.Marshaler) { @@ -10960,10 +10738,19 @@ func (ec *executionContext) _AddCommentTimelineItem(ctx context.Context, sel ast return res }) case "author": - out.Values[i] = ec._AddCommentTimelineItem_author(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_author(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&invalids, 1) + } + return res + }) case "message": out.Values[i] = ec._AddCommentTimelineItem_message(ctx, field, obj) if out.Values[i] == graphql.Null { @@ -11030,7 +10817,7 @@ func (ec *executionContext) _AddCommentTimelineItem(ctx context.Context, sel ast var bugImplementors = []string{"Bug", "Authored"} -func (ec *executionContext) _Bug(ctx context.Context, sel ast.SelectionSet, obj *bug.Snapshot) graphql.Marshaler { +func (ec *executionContext) _Bug(ctx context.Context, sel ast.SelectionSet, obj models.BugWrapper) graphql.Marshaler { fields := graphql.CollectFields(ec.OperationContext, sel, bugImplementors) out := graphql.NewFieldSet(fields) @@ -11102,19 +10889,10 @@ func (ec *executionContext) _Bug(ctx context.Context, sel ast.SelectionSet, obj atomic.AddUint32(&invalids, 1) } case "lastEdit": - 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_lastEdit(ctx, field, obj) - if res == graphql.Null { - atomic.AddUint32(&invalids, 1) - } - return res - }) + out.Values[i] = ec._Bug_lastEdit(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&invalids, 1) + } case "actors": field := field out.Concurrently(i, func() (res graphql.Marshaler) { @@ -11419,19 +11197,28 @@ func (ec *executionContext) _Comment(ctx context.Context, sel ast.SelectionSet, case "__typename": out.Values[i] = graphql.MarshalString("Comment") case "author": - out.Values[i] = ec._Comment_author(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ - } + 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._Comment_author(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&invalids, 1) + } + return res + }) case "message": out.Values[i] = ec._Comment_message(ctx, field, obj) if out.Values[i] == graphql.Null { - invalids++ + atomic.AddUint32(&invalids, 1) } case "files": out.Values[i] = ec._Comment_files(ctx, field, obj) if out.Values[i] == graphql.Null { - invalids++ + atomic.AddUint32(&invalids, 1) } default: panic("unknown field " + strconv.Quote(field.Name)) @@ -11643,10 +11430,19 @@ func (ec *executionContext) _CreateOperation(ctx context.Context, sel ast.Select return res }) case "author": - out.Values[i] = ec._CreateOperation_author(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_author(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&invalids, 1) + } + return res + }) case "date": field := field out.Concurrently(i, func() (res graphql.Marshaler) { @@ -11713,10 +11509,19 @@ func (ec *executionContext) _CreateTimelineItem(ctx context.Context, sel ast.Sel return res }) case "author": - out.Values[i] = ec._CreateTimelineItem_author(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_author(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&invalids, 1) + } + return res + }) case "message": out.Values[i] = ec._CreateTimelineItem_message(ctx, field, obj) if out.Values[i] == graphql.Null { @@ -11807,10 +11612,19 @@ func (ec *executionContext) _EditCommentOperation(ctx context.Context, sel ast.S return res }) case "author": - out.Values[i] = ec._EditCommentOperation_author(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_author(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&invalids, 1) + } + return res + }) case "date": field := field out.Concurrently(i, func() (res graphql.Marshaler) { @@ -11862,7 +11676,7 @@ func (ec *executionContext) _EditCommentOperation(ctx context.Context, sel ast.S var identityImplementors = []string{"Identity"} -func (ec *executionContext) _Identity(ctx context.Context, sel ast.SelectionSet, obj identity.Interface) graphql.Marshaler { +func (ec *executionContext) _Identity(ctx context.Context, sel ast.SelectionSet, obj models.IdentityWrapper) graphql.Marshaler { fields := graphql.CollectFields(ec.OperationContext, sel, identityImplementors) out := graphql.NewFieldSet(fields) @@ -12076,10 +11890,19 @@ func (ec *executionContext) _LabelChangeOperation(ctx context.Context, sel ast.S return res }) case "author": - out.Values[i] = ec._LabelChangeOperation_author(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_author(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&invalids, 1) + } + return res + }) case "date": field := field out.Concurrently(i, func() (res graphql.Marshaler) { @@ -12182,10 +12005,19 @@ func (ec *executionContext) _LabelChangeTimelineItem(ctx context.Context, sel as return res }) case "author": - out.Values[i] = ec._LabelChangeTimelineItem_author(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_author(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&invalids, 1) + } + return res + }) case "date": field := field out.Concurrently(i, func() (res graphql.Marshaler) { @@ -12720,10 +12552,19 @@ func (ec *executionContext) _SetStatusOperation(ctx context.Context, sel ast.Sel return res }) case "author": - out.Values[i] = ec._SetStatusOperation_author(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_author(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&invalids, 1) + } + return res + }) case "date": field := field out.Concurrently(i, func() (res graphql.Marshaler) { @@ -12789,10 +12630,19 @@ func (ec *executionContext) _SetStatusTimelineItem(ctx context.Context, sel ast. return res }) case "author": - out.Values[i] = ec._SetStatusTimelineItem_author(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_author(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&invalids, 1) + } + return res + }) case "date": field := field out.Concurrently(i, func() (res graphql.Marshaler) { @@ -12858,10 +12708,19 @@ func (ec *executionContext) _SetTitleOperation(ctx context.Context, sel ast.Sele return res }) case "author": - out.Values[i] = ec._SetTitleOperation_author(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_author(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&invalids, 1) + } + return res + }) case "date": field := field out.Concurrently(i, func() (res graphql.Marshaler) { @@ -12957,10 +12816,19 @@ func (ec *executionContext) _SetTitleTimelineItem(ctx context.Context, sel ast.S return res }) case "author": - out.Values[i] = ec._SetTitleTimelineItem_author(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_author(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&invalids, 1) + } + return res + }) case "date": field := field out.Concurrently(i, func() (res graphql.Marshaler) { @@ -13361,11 +13229,17 @@ func (ec *executionContext) marshalNBoolean2bool(ctx context.Context, sel ast.Se return res } -func (ec *executionContext) marshalNBug2githubáš—comáš‹MichaelMureáš‹gitáš‘bugáš‹bugášSnapshot(ctx context.Context, sel ast.SelectionSet, v bug.Snapshot) graphql.Marshaler { - return ec._Bug(ctx, sel, &v) +func (ec *executionContext) marshalNBug2githubáš—comáš‹MichaelMureáš‹gitáš‘bugáš‹graphqláš‹modelsášBugWrapper(ctx context.Context, sel ast.SelectionSet, v models.BugWrapper) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + return ec._Bug(ctx, sel, v) } -func (ec *executionContext) marshalNBug2áš•áš–githubáš—comáš‹MichaelMureáš‹gitáš‘bugáš‹bugášSnapshotáš„(ctx context.Context, sel ast.SelectionSet, v []*bug.Snapshot) graphql.Marshaler { +func (ec *executionContext) marshalNBug2áš•githubáš—comáš‹MichaelMureáš‹gitáš‘bugáš‹graphqláš‹modelsášBugWrapperáš„(ctx context.Context, sel ast.SelectionSet, v []models.BugWrapper) graphql.Marshaler { ret := make(graphql.Array, len(v)) var wg sync.WaitGroup isLen1 := len(v) == 1 @@ -13389,7 +13263,7 @@ func (ec *executionContext) marshalNBug2áš•áš–githubáš—comáš‹MichaelMureáš‹gitáš if !isLen1 { defer wg.Done() } - ret[i] = ec.marshalNBug2áš–githubáš—comáš‹MichaelMureáš‹gitáš‘bugáš‹bugášSnapshot(ctx, sel, v[i]) + ret[i] = ec.marshalNBug2githubáš—comáš‹MichaelMureáš‹gitáš‘bugáš‹graphqláš‹modelsášBugWrapper(ctx, sel, v[i]) } if isLen1 { f(i) @@ -13402,16 +13276,6 @@ func (ec *executionContext) marshalNBug2áš•áš–githubáš—comáš‹MichaelMureáš‹gitáš return ret } -func (ec *executionContext) marshalNBug2áš–githubáš—comáš‹MichaelMureáš‹gitáš‘bugáš‹bugášSnapshot(ctx context.Context, sel ast.SelectionSet, v *bug.Snapshot) graphql.Marshaler { - if v == nil { - if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { - ec.Errorf(ctx, "must not be null") - } - return graphql.Null - } - return ec._Bug(ctx, sel, v) -} - func (ec *executionContext) marshalNBugConnection2githubáš—comáš‹MichaelMureáš‹gitáš‘bugáš‹graphqláš‹modelsášBugConnection(ctx context.Context, sel ast.SelectionSet, v models.BugConnection) graphql.Marshaler { return ec._BugConnection(ctx, sel, &v) } @@ -13768,7 +13632,7 @@ func (ec *executionContext) marshalNHash2áš•githubáš—comáš‹MichaelMureáš‹gitáš‘b return ret } -func (ec *executionContext) marshalNIdentity2githubáš—comáš‹MichaelMureáš‹gitáš‘bugáš‹identityášInterface(ctx context.Context, sel ast.SelectionSet, v identity.Interface) graphql.Marshaler { +func (ec *executionContext) marshalNIdentity2githubáš—comáš‹MichaelMureáš‹gitáš‘bugáš‹graphqláš‹modelsášIdentityWrapper(ctx context.Context, sel ast.SelectionSet, v models.IdentityWrapper) graphql.Marshaler { if v == nil { if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { ec.Errorf(ctx, "must not be null") @@ -13778,7 +13642,7 @@ func (ec *executionContext) marshalNIdentity2githubáš—comáš‹MichaelMureáš‹gitáš‘ return ec._Identity(ctx, sel, v) } -func (ec *executionContext) marshalNIdentity2áš•githubáš—comáš‹MichaelMureáš‹gitáš‘bugáš‹identityášInterfaceáš„(ctx context.Context, sel ast.SelectionSet, v []identity.Interface) graphql.Marshaler { +func (ec *executionContext) marshalNIdentity2áš•githubáš—comáš‹MichaelMureáš‹gitáš‘bugáš‹graphqláš‹modelsášIdentityWrapperáš„(ctx context.Context, sel ast.SelectionSet, v []models.IdentityWrapper) graphql.Marshaler { ret := make(graphql.Array, len(v)) var wg sync.WaitGroup isLen1 := len(v) == 1 @@ -13802,7 +13666,7 @@ func (ec *executionContext) marshalNIdentity2áš•githubáš—comáš‹MichaelMureáš‹git if !isLen1 { defer wg.Done() } - ret[i] = ec.marshalNIdentity2githubáš—comáš‹MichaelMureáš‹gitáš‘bugáš‹identityášInterface(ctx, sel, v[i]) + ret[i] = ec.marshalNIdentity2githubáš—comáš‹MichaelMureáš‹gitáš‘bugáš‹graphqláš‹modelsášIdentityWrapper(ctx, sel, v[i]) } if isLen1 { f(i) @@ -14684,11 +14548,7 @@ func (ec *executionContext) marshalOBoolean2áš–bool(ctx context.Context, sel ast return ec.marshalOBoolean2bool(ctx, sel, *v) } -func (ec *executionContext) marshalOBug2githubáš—comáš‹MichaelMureáš‹gitáš‘bugáš‹bugášSnapshot(ctx context.Context, sel ast.SelectionSet, v bug.Snapshot) graphql.Marshaler { - return ec._Bug(ctx, sel, &v) -} - -func (ec *executionContext) marshalOBug2áš–githubáš—comáš‹MichaelMureáš‹gitáš‘bugáš‹bugášSnapshot(ctx context.Context, sel ast.SelectionSet, v *bug.Snapshot) graphql.Marshaler { +func (ec *executionContext) marshalOBug2githubáš—comáš‹MichaelMureáš‹gitáš‘bugáš‹graphqláš‹modelsášBugWrapper(ctx context.Context, sel ast.SelectionSet, v models.BugWrapper) graphql.Marshaler { if v == nil { return graphql.Null } @@ -14739,7 +14599,7 @@ func (ec *executionContext) marshalOHash2áš•githubáš—comáš‹MichaelMureáš‹gitáš‘b return ret } -func (ec *executionContext) marshalOIdentity2githubáš—comáš‹MichaelMureáš‹gitáš‘bugáš‹identityášInterface(ctx context.Context, sel ast.SelectionSet, v identity.Interface) graphql.Marshaler { +func (ec *executionContext) marshalOIdentity2githubáš—comáš‹MichaelMureáš‹gitáš‘bugáš‹graphqláš‹modelsášIdentityWrapper(ctx context.Context, sel ast.SelectionSet, v models.IdentityWrapper) graphql.Marshaler { if v == nil { return graphql.Null } diff --git a/graphql/models/gen_models.go b/graphql/models/gen_models.go index 5498960b..b3e14655 100644 --- a/graphql/models/gen_models.go +++ b/graphql/models/gen_models.go @@ -8,7 +8,6 @@ import ( "strconv" "github.com/MichaelMure/git-bug/bug" - "github.com/MichaelMure/git-bug/identity" "github.com/MichaelMure/git-bug/util/git" ) @@ -34,7 +33,7 @@ type AddCommentPayload struct { // A unique identifier for the client performing the mutation. ClientMutationID *string `json:"clientMutationId"` // The affected bug. - Bug *bug.Snapshot `json:"bug"` + Bug BugWrapper `json:"bug"` // The resulting operation. Operation *bug.AddCommentOperation `json:"operation"` } @@ -42,8 +41,8 @@ type AddCommentPayload struct { // The connection type for Bug. type BugConnection struct { // A list of edges. - Edges []*BugEdge `json:"edges"` - Nodes []*bug.Snapshot `json:"nodes"` + Edges []*BugEdge `json:"edges"` + Nodes []BugWrapper `json:"nodes"` // Information to aid in pagination. PageInfo *PageInfo `json:"pageInfo"` // Identifies the total count of items in the connection. @@ -55,7 +54,7 @@ type BugEdge struct { // A cursor for use in pagination. Cursor string `json:"cursor"` // The item at the end of the edge. - Node *bug.Snapshot `json:"node"` + Node BugWrapper `json:"node"` } type ChangeLabelInput struct { @@ -75,7 +74,7 @@ type ChangeLabelPayload struct { // A unique identifier for the client performing the mutation. ClientMutationID *string `json:"clientMutationId"` // The affected bug. - Bug *bug.Snapshot `json:"bug"` + Bug BugWrapper `json:"bug"` // The resulting operation. Operation *bug.LabelChangeOperation `json:"operation"` // The effect each source label had. @@ -95,7 +94,7 @@ type CloseBugPayload struct { // A unique identifier for the client performing the mutation. ClientMutationID *string `json:"clientMutationId"` // The affected bug. - Bug *bug.Snapshot `json:"bug"` + Bug BugWrapper `json:"bug"` // The resulting operation. Operation *bug.SetStatusOperation `json:"operation"` } @@ -125,7 +124,7 @@ type CommitAsNeededPayload struct { // A unique identifier for the client performing the mutation. ClientMutationID *string `json:"clientMutationId"` // The affected bug. - Bug *bug.Snapshot `json:"bug"` + Bug BugWrapper `json:"bug"` } type CommitInput struct { @@ -141,19 +140,19 @@ type CommitPayload struct { // A unique identifier for the client performing the mutation. ClientMutationID *string `json:"clientMutationId"` // The affected bug. - Bug *bug.Snapshot `json:"bug"` + Bug BugWrapper `json:"bug"` } type IdentityConnection struct { - Edges []*IdentityEdge `json:"edges"` - Nodes []identity.Interface `json:"nodes"` - PageInfo *PageInfo `json:"pageInfo"` - TotalCount int `json:"totalCount"` + Edges []*IdentityEdge `json:"edges"` + Nodes []IdentityWrapper `json:"nodes"` + PageInfo *PageInfo `json:"pageInfo"` + TotalCount int `json:"totalCount"` } type IdentityEdge struct { - Cursor string `json:"cursor"` - Node identity.Interface `json:"node"` + Cursor string `json:"cursor"` + Node IdentityWrapper `json:"node"` } type LabelConnection struct { @@ -185,7 +184,7 @@ type NewBugPayload struct { // A unique identifier for the client performing the mutation. ClientMutationID *string `json:"clientMutationId"` // The created bug. - Bug *bug.Snapshot `json:"bug"` + Bug BugWrapper `json:"bug"` // The resulting operation. Operation *bug.CreateOperation `json:"operation"` } @@ -203,7 +202,7 @@ type OpenBugPayload struct { // A unique identifier for the client performing the mutation. ClientMutationID *string `json:"clientMutationId"` // The affected bug. - Bug *bug.Snapshot `json:"bug"` + Bug BugWrapper `json:"bug"` // The resulting operation. Operation *bug.SetStatusOperation `json:"operation"` } @@ -249,7 +248,7 @@ type SetTitlePayload struct { // A unique identifier for the client performing the mutation. ClientMutationID *string `json:"clientMutationId"` // The affected bug. - Bug *bug.Snapshot `json:"bug"` + Bug BugWrapper `json:"bug"` // The resulting operation Operation *bug.SetTitleOperation `json:"operation"` } diff --git a/graphql/models/lazy_bug.go b/graphql/models/lazy_bug.go new file mode 100644 index 00000000..6034e80d --- /dev/null +++ b/graphql/models/lazy_bug.go @@ -0,0 +1,215 @@ +package models + +import ( + "sync" + "time" + + "github.com/MichaelMure/git-bug/bug" + "github.com/MichaelMure/git-bug/cache" + "github.com/MichaelMure/git-bug/entity" +) + +// BugWrapper is an interface used by the GraphQL resolvers to handle a bug. +// Depending on the situation, a Bug can already be fully loaded in memory or not. +// This interface is used to wrap either a lazyBug or a loadedBug depending on the situation. +type BugWrapper interface { + Id() entity.Id + LastEdit() time.Time + Status() bug.Status + Title() string + Comments() ([]bug.Comment, error) + Labels() []bug.Label + Author() (IdentityWrapper, error) + Actors() ([]IdentityWrapper, error) + Participants() ([]IdentityWrapper, error) + CreatedAt() time.Time + Timeline() ([]bug.TimelineItem, error) + Operations() ([]bug.Operation, error) + + IsAuthored() +} + +var _ BugWrapper = &lazyBug{} + +// lazyBug is a lazy-loading wrapper that fetch data from the cache (BugExcerpt) in priority, +// and load the complete bug and snapshot only when necessary. +type lazyBug struct { + cache *cache.RepoCache + excerpt *cache.BugExcerpt + + mu sync.Mutex + snap *bug.Snapshot +} + +func NewLazyBug(cache *cache.RepoCache, excerpt *cache.BugExcerpt) *lazyBug { + return &lazyBug{ + cache: cache, + excerpt: excerpt, + } +} + +func (lb *lazyBug) load() error { + if lb.snap != nil { + return nil + } + + lb.mu.Lock() + defer lb.mu.Unlock() + + b, err := lb.cache.ResolveBug(lb.excerpt.Id) + if err != nil { + return err + } + + lb.snap = b.Snapshot() + return nil +} + +func (lb *lazyBug) identity(id entity.Id) (IdentityWrapper, error) { + i, err := lb.cache.ResolveIdentityExcerpt(id) + if err != nil { + return nil, err + } + return &lazyIdentity{cache: lb.cache, excerpt: i}, nil +} + +// Sign post method for gqlgen +func (lb *lazyBug) IsAuthored() {} + +func (lb *lazyBug) Id() entity.Id { + return lb.excerpt.Id +} + +func (lb *lazyBug) LastEdit() time.Time { + return time.Unix(lb.excerpt.EditUnixTime, 0) +} + +func (lb *lazyBug) Status() bug.Status { + return lb.excerpt.Status +} + +func (lb *lazyBug) Title() string { + return lb.excerpt.Title +} + +func (lb *lazyBug) Comments() ([]bug.Comment, error) { + err := lb.load() + if err != nil { + return nil, err + } + return lb.snap.Comments, nil +} + +func (lb *lazyBug) Labels() []bug.Label { + return lb.excerpt.Labels +} + +func (lb *lazyBug) Author() (IdentityWrapper, error) { + return lb.identity(lb.excerpt.AuthorId) +} + +func (lb *lazyBug) Actors() ([]IdentityWrapper, error) { + result := make([]IdentityWrapper, len(lb.excerpt.Actors)) + for i, actorId := range lb.excerpt.Actors { + actor, err := lb.identity(actorId) + if err != nil { + return nil, err + } + result[i] = actor + } + return result, nil +} + +func (lb *lazyBug) Participants() ([]IdentityWrapper, error) { + result := make([]IdentityWrapper, len(lb.excerpt.Participants)) + for i, participantId := range lb.excerpt.Participants { + participant, err := lb.identity(participantId) + if err != nil { + return nil, err + } + result[i] = participant + } + return result, nil +} + +func (lb *lazyBug) CreatedAt() time.Time { + return time.Unix(lb.excerpt.CreateUnixTime, 0) +} + +func (lb *lazyBug) Timeline() ([]bug.TimelineItem, error) { + err := lb.load() + if err != nil { + return nil, err + } + return lb.snap.Timeline, nil +} + +func (lb *lazyBug) Operations() ([]bug.Operation, error) { + err := lb.load() + if err != nil { + return nil, err + } + return lb.snap.Operations, nil +} + +var _ BugWrapper = &loadedBug{} + +type loadedBug struct { + *bug.Snapshot +} + +func NewLoadedBug(snap *bug.Snapshot) *loadedBug { + return &loadedBug{Snapshot: snap} +} + +func (l *loadedBug) LastEdit() time.Time { + return l.Snapshot.LastEditTime() +} + +func (l *loadedBug) Status() bug.Status { + return l.Snapshot.Status +} + +func (l *loadedBug) Title() string { + return l.Snapshot.Title +} + +func (l *loadedBug) Comments() ([]bug.Comment, error) { + return l.Snapshot.Comments, nil +} + +func (l *loadedBug) Labels() []bug.Label { + return l.Snapshot.Labels +} + +func (l *loadedBug) Author() (IdentityWrapper, error) { + return NewLoadedIdentity(l.Snapshot.Author), nil +} + +func (l *loadedBug) Actors() ([]IdentityWrapper, error) { + res := make([]IdentityWrapper, len(l.Snapshot.Actors)) + for i, actor := range l.Snapshot.Actors { + res[i] = NewLoadedIdentity(actor) + } + return res, nil +} + +func (l *loadedBug) Participants() ([]IdentityWrapper, error) { + res := make([]IdentityWrapper, len(l.Snapshot.Participants)) + for i, participant := range l.Snapshot.Participants { + res[i] = NewLoadedIdentity(participant) + } + return res, nil +} + +func (l *loadedBug) CreatedAt() time.Time { + return l.Snapshot.CreatedAt +} + +func (l *loadedBug) Timeline() ([]bug.TimelineItem, error) { + return l.Snapshot.Timeline, nil +} + +func (l *loadedBug) Operations() ([]bug.Operation, error) { + return l.Snapshot.Operations, nil +} diff --git a/graphql/models/lazy_identity.go b/graphql/models/lazy_identity.go new file mode 100644 index 00000000..bbd36be3 --- /dev/null +++ b/graphql/models/lazy_identity.go @@ -0,0 +1,167 @@ +package models + +import ( + "fmt" + "sync" + + "github.com/MichaelMure/git-bug/cache" + "github.com/MichaelMure/git-bug/entity" + "github.com/MichaelMure/git-bug/identity" + "github.com/MichaelMure/git-bug/util/lamport" + "github.com/MichaelMure/git-bug/util/timestamp" +) + +// IdentityWrapper is an interface used by the GraphQL resolvers to handle an identity. +// Depending on the situation, an Identity can already be fully loaded in memory or not. +// This interface is used to wrap either a lazyIdentity or a loadedIdentity depending on the situation. +type IdentityWrapper interface { + Id() entity.Id + Name() string + Email() (string, error) + AvatarUrl() (string, error) + Keys() ([]*identity.Key, error) + ValidKeysAtTime(time lamport.Time) ([]*identity.Key, error) + DisplayName() string + IsProtected() (bool, error) + LastModificationLamport() (lamport.Time, error) + LastModification() (timestamp.Timestamp, error) +} + +var _ IdentityWrapper = &lazyIdentity{} + +type lazyIdentity struct { + cache *cache.RepoCache + excerpt *cache.IdentityExcerpt + + mu sync.Mutex + id *cache.IdentityCache +} + +func NewLazyIdentity(cache *cache.RepoCache, excerpt *cache.IdentityExcerpt) *lazyIdentity { + return &lazyIdentity{ + cache: cache, + excerpt: excerpt, + } +} + +func (li *lazyIdentity) load() (*cache.IdentityCache, error) { + if li.id != nil { + return li.id, nil + } + + li.mu.Lock() + defer li.mu.Unlock() + + id, err := li.cache.ResolveIdentity(li.excerpt.Id) + if err != nil { + return nil, fmt.Errorf("cache: missing identity %v", li.excerpt.Id) + } + li.id = id + return id, nil +} + +func (li *lazyIdentity) Id() entity.Id { + return li.excerpt.Id +} + +func (li *lazyIdentity) Name() string { + return li.excerpt.Name +} + +func (li *lazyIdentity) Email() (string, error) { + id, err := li.load() + if err != nil { + return "", err + } + return id.Email(), nil +} + +func (li *lazyIdentity) AvatarUrl() (string, error) { + id, err := li.load() + if err != nil { + return "", err + } + return id.AvatarUrl(), nil +} + +func (li *lazyIdentity) Keys() ([]*identity.Key, error) { + id, err := li.load() + if err != nil { + return nil, err + } + return id.Keys(), nil +} + +func (li *lazyIdentity) ValidKeysAtTime(time lamport.Time) ([]*identity.Key, error) { + id, err := li.load() + if err != nil { + return nil, err + } + return id.ValidKeysAtTime(time), nil +} + +func (li *lazyIdentity) DisplayName() string { + return li.excerpt.DisplayName() +} + +func (li *lazyIdentity) IsProtected() (bool, error) { + id, err := li.load() + if err != nil { + return false, err + } + return id.IsProtected(), nil +} + +func (li *lazyIdentity) LastModificationLamport() (lamport.Time, error) { + id, err := li.load() + if err != nil { + return 0, err + } + return id.LastModificationLamport(), nil +} + +func (li *lazyIdentity) LastModification() (timestamp.Timestamp, error) { + id, err := li.load() + if err != nil { + return 0, err + } + return id.LastModification(), nil +} + +var _ IdentityWrapper = &loadedIdentity{} + +type loadedIdentity struct { + identity.Interface +} + +func NewLoadedIdentity(id identity.Interface) *loadedIdentity { + return &loadedIdentity{Interface: id} +} + +func (l loadedIdentity) Email() (string, error) { + return l.Interface.Email(), nil +} + +func (l loadedIdentity) AvatarUrl() (string, error) { + return l.Interface.AvatarUrl(), nil +} + +func (l loadedIdentity) Keys() ([]*identity.Key, error) { + return l.Interface.Keys(), nil +} + +func (l loadedIdentity) ValidKeysAtTime(time lamport.Time) ([]*identity.Key, error) { + return l.Interface.ValidKeysAtTime(time), nil +} + +func (l loadedIdentity) IsProtected() (bool, error) { + return l.Interface.IsProtected(), nil +} + +func (l loadedIdentity) LastModificationLamport() (lamport.Time, error) { + return l.Interface.LastModificationLamport(), nil +} + +func (l loadedIdentity) LastModification() (timestamp.Timestamp, error) { + return l.Interface.LastModification(), nil +} diff --git a/graphql/resolvers/bug.go b/graphql/resolvers/bug.go index 8f994c0b..fd8f4b6e 100644 --- a/graphql/resolvers/bug.go +++ b/graphql/resolvers/bug.go @@ -2,32 +2,30 @@ package resolvers import ( "context" - "time" "github.com/MichaelMure/git-bug/bug" "github.com/MichaelMure/git-bug/graphql/connections" "github.com/MichaelMure/git-bug/graphql/graph" "github.com/MichaelMure/git-bug/graphql/models" - "github.com/MichaelMure/git-bug/identity" ) var _ graph.BugResolver = &bugResolver{} type bugResolver struct{} -func (bugResolver) ID(ctx context.Context, obj *bug.Snapshot) (string, error) { +func (bugResolver) ID(_ context.Context, obj models.BugWrapper) (string, error) { return obj.Id().String(), nil } -func (bugResolver) HumanID(ctx context.Context, obj *bug.Snapshot) (string, error) { +func (bugResolver) HumanID(_ context.Context, obj models.BugWrapper) (string, error) { return obj.Id().Human(), nil } -func (bugResolver) Status(ctx context.Context, obj *bug.Snapshot) (models.Status, error) { - return convertStatus(obj.Status) +func (bugResolver) Status(_ context.Context, obj models.BugWrapper) (models.Status, error) { + return convertStatus(obj.Status()) } -func (bugResolver) Comments(ctx context.Context, obj *bug.Snapshot, after *string, before *string, first *int, last *int) (*models.CommentConnection, error) { +func (bugResolver) Comments(_ context.Context, obj models.BugWrapper, after *string, before *string, first *int, last *int) (*models.CommentConnection, error) { input := models.ConnectionInput{ Before: before, After: after, @@ -55,10 +53,15 @@ func (bugResolver) Comments(ctx context.Context, obj *bug.Snapshot, after *strin }, nil } - return connections.CommentCon(obj.Comments, edger, conMaker, input) + comments, err := obj.Comments() + if err != nil { + return nil, err + } + + return connections.CommentCon(comments, edger, conMaker, input) } -func (bugResolver) Operations(ctx context.Context, obj *bug.Snapshot, after *string, before *string, first *int, last *int) (*models.OperationConnection, error) { +func (bugResolver) Operations(_ context.Context, obj models.BugWrapper, after *string, before *string, first *int, last *int) (*models.OperationConnection, error) { input := models.ConnectionInput{ Before: before, After: after, @@ -82,10 +85,15 @@ func (bugResolver) Operations(ctx context.Context, obj *bug.Snapshot, after *str }, nil } - return connections.OperationCon(obj.Operations, edger, conMaker, input) + ops, err := obj.Operations() + if err != nil { + return nil, err + } + + return connections.OperationCon(ops, edger, conMaker, input) } -func (bugResolver) Timeline(ctx context.Context, obj *bug.Snapshot, after *string, before *string, first *int, last *int) (*models.TimelineItemConnection, error) { +func (bugResolver) Timeline(_ context.Context, obj models.BugWrapper, after *string, before *string, first *int, last *int) (*models.TimelineItemConnection, error) { input := models.ConnectionInput{ Before: before, After: after, @@ -109,15 +117,15 @@ func (bugResolver) Timeline(ctx context.Context, obj *bug.Snapshot, after *strin }, nil } - return connections.TimelineItemCon(obj.Timeline, edger, conMaker, input) -} + timeline, err := obj.Timeline() + if err != nil { + return nil, err + } -func (bugResolver) LastEdit(ctx context.Context, obj *bug.Snapshot) (*time.Time, error) { - t := obj.LastEditTime() - return &t, nil + return connections.TimelineItemCon(timeline, edger, conMaker, input) } -func (bugResolver) Actors(ctx context.Context, obj *bug.Snapshot, after *string, before *string, first *int, last *int) (*models.IdentityConnection, error) { +func (bugResolver) Actors(_ context.Context, obj models.BugWrapper, after *string, before *string, first *int, last *int) (*models.IdentityConnection, error) { input := models.ConnectionInput{ Before: before, After: after, @@ -125,14 +133,14 @@ func (bugResolver) Actors(ctx context.Context, obj *bug.Snapshot, after *string, Last: last, } - edger := func(actor identity.Interface, offset int) connections.Edge { + edger := func(actor models.IdentityWrapper, offset int) connections.Edge { return models.IdentityEdge{ Node: actor, Cursor: connections.OffsetToCursor(offset), } } - conMaker := func(edges []*models.IdentityEdge, nodes []identity.Interface, info *models.PageInfo, totalCount int) (*models.IdentityConnection, error) { + conMaker := func(edges []*models.IdentityEdge, nodes []models.IdentityWrapper, info *models.PageInfo, totalCount int) (*models.IdentityConnection, error) { return &models.IdentityConnection{ Edges: edges, Nodes: nodes, @@ -141,10 +149,15 @@ func (bugResolver) Actors(ctx context.Context, obj *bug.Snapshot, after *string, }, nil } - return connections.IdentityCon(obj.Actors, edger, conMaker, input) + actors, err := obj.Actors() + if err != nil { + return nil, err + } + + return connections.IdentityCon(actors, edger, conMaker, input) } -func (bugResolver) Participants(ctx context.Context, obj *bug.Snapshot, after *string, before *string, first *int, last *int) (*models.IdentityConnection, error) { +func (bugResolver) Participants(_ context.Context, obj models.BugWrapper, after *string, before *string, first *int, last *int) (*models.IdentityConnection, error) { input := models.ConnectionInput{ Before: before, After: after, @@ -152,14 +165,14 @@ func (bugResolver) Participants(ctx context.Context, obj *bug.Snapshot, after *s Last: last, } - edger := func(participant identity.Interface, offset int) connections.Edge { + edger := func(participant models.IdentityWrapper, offset int) connections.Edge { return models.IdentityEdge{ Node: participant, Cursor: connections.OffsetToCursor(offset), } } - conMaker := func(edges []*models.IdentityEdge, nodes []identity.Interface, info *models.PageInfo, totalCount int) (*models.IdentityConnection, error) { + conMaker := func(edges []*models.IdentityEdge, nodes []models.IdentityWrapper, info *models.PageInfo, totalCount int) (*models.IdentityConnection, error) { return &models.IdentityConnection{ Edges: edges, Nodes: nodes, @@ -168,5 +181,10 @@ func (bugResolver) Participants(ctx context.Context, obj *bug.Snapshot, after *s }, nil } - return connections.IdentityCon(obj.Participants, edger, conMaker, input) + participants, err := obj.Participants() + if err != nil { + return nil, err + } + + return connections.IdentityCon(participants, edger, conMaker, input) } diff --git a/graphql/resolvers/color.go b/graphql/resolvers/color.go index dc6f1b9c..8dc13095 100644 --- a/graphql/resolvers/color.go +++ b/graphql/resolvers/color.go @@ -11,14 +11,14 @@ var _ graph.ColorResolver = &colorResolver{} type colorResolver struct{} -func (colorResolver) R(ctx context.Context, obj *color.RGBA) (int, error) { +func (colorResolver) R(_ context.Context, obj *color.RGBA) (int, error) { return int(obj.R), nil } -func (colorResolver) G(ctx context.Context, obj *color.RGBA) (int, error) { +func (colorResolver) G(_ context.Context, obj *color.RGBA) (int, error) { return int(obj.G), nil } -func (colorResolver) B(ctx context.Context, obj *color.RGBA) (int, error) { +func (colorResolver) B(_ context.Context, obj *color.RGBA) (int, error) { return int(obj.B), nil } diff --git a/graphql/resolvers/comment.go b/graphql/resolvers/comment.go new file mode 100644 index 00000000..b142712a --- /dev/null +++ b/graphql/resolvers/comment.go @@ -0,0 +1,17 @@ +package resolvers + +import ( + "context" + + "github.com/MichaelMure/git-bug/bug" + "github.com/MichaelMure/git-bug/graphql/graph" + "github.com/MichaelMure/git-bug/graphql/models" +) + +var _ graph.CommentResolver = &commentResolver{} + +type commentResolver struct{} + +func (c commentResolver) Author(_ context.Context, obj *bug.Comment) (models.IdentityWrapper, error) { + return models.NewLoadedIdentity(obj.Author), nil +} diff --git a/graphql/resolvers/identity.go b/graphql/resolvers/identity.go index d36669d0..b8aa72a7 100644 --- a/graphql/resolvers/identity.go +++ b/graphql/resolvers/identity.go @@ -4,18 +4,18 @@ import ( "context" "github.com/MichaelMure/git-bug/graphql/graph" - "github.com/MichaelMure/git-bug/identity" + "github.com/MichaelMure/git-bug/graphql/models" ) var _ graph.IdentityResolver = &identityResolver{} type identityResolver struct{} -func (identityResolver) ID(ctx context.Context, obj identity.Interface) (string, error) { +func (identityResolver) ID(ctx context.Context, obj models.IdentityWrapper) (string, error) { return obj.Id().String(), nil } -func (r identityResolver) HumanID(ctx context.Context, obj identity.Interface) (string, error) { +func (r identityResolver) HumanID(ctx context.Context, obj models.IdentityWrapper) (string, error) { return obj.Id().Human(), nil } diff --git a/graphql/resolvers/mutation.go b/graphql/resolvers/mutation.go index b4f8845a..889edbc8 100644 --- a/graphql/resolvers/mutation.go +++ b/graphql/resolvers/mutation.go @@ -23,7 +23,7 @@ func (r mutationResolver) getRepo(ref *string) (*cache.RepoCache, error) { return r.cache.DefaultRepo() } -func (r mutationResolver) NewBug(ctx context.Context, input models.NewBugInput) (*models.NewBugPayload, error) { +func (r mutationResolver) NewBug(_ context.Context, input models.NewBugInput) (*models.NewBugPayload, error) { repo, err := r.getRepo(input.RepoRef) if err != nil { return nil, err @@ -36,12 +36,12 @@ func (r mutationResolver) NewBug(ctx context.Context, input models.NewBugInput) return &models.NewBugPayload{ ClientMutationID: input.ClientMutationID, - Bug: b.Snapshot(), + Bug: models.NewLoadedBug(b.Snapshot()), Operation: op, }, nil } -func (r mutationResolver) AddComment(ctx context.Context, input models.AddCommentInput) (*models.AddCommentPayload, error) { +func (r mutationResolver) AddComment(_ context.Context, input models.AddCommentInput) (*models.AddCommentPayload, error) { repo, err := r.getRepo(input.RepoRef) if err != nil { return nil, err @@ -59,12 +59,12 @@ func (r mutationResolver) AddComment(ctx context.Context, input models.AddCommen return &models.AddCommentPayload{ ClientMutationID: input.ClientMutationID, - Bug: b.Snapshot(), + Bug: models.NewLoadedBug(b.Snapshot()), Operation: op, }, nil } -func (r mutationResolver) ChangeLabels(ctx context.Context, input *models.ChangeLabelInput) (*models.ChangeLabelPayload, error) { +func (r mutationResolver) ChangeLabels(_ context.Context, input *models.ChangeLabelInput) (*models.ChangeLabelPayload, error) { repo, err := r.getRepo(input.RepoRef) if err != nil { return nil, err @@ -87,13 +87,13 @@ func (r mutationResolver) ChangeLabels(ctx context.Context, input *models.Change return &models.ChangeLabelPayload{ ClientMutationID: input.ClientMutationID, - Bug: b.Snapshot(), + Bug: models.NewLoadedBug(b.Snapshot()), Operation: op, Results: resultsPtr, }, nil } -func (r mutationResolver) OpenBug(ctx context.Context, input models.OpenBugInput) (*models.OpenBugPayload, error) { +func (r mutationResolver) OpenBug(_ context.Context, input models.OpenBugInput) (*models.OpenBugPayload, error) { repo, err := r.getRepo(input.RepoRef) if err != nil { return nil, err @@ -111,12 +111,12 @@ func (r mutationResolver) OpenBug(ctx context.Context, input models.OpenBugInput return &models.OpenBugPayload{ ClientMutationID: input.ClientMutationID, - Bug: b.Snapshot(), + Bug: models.NewLoadedBug(b.Snapshot()), Operation: op, }, nil } -func (r mutationResolver) CloseBug(ctx context.Context, input models.CloseBugInput) (*models.CloseBugPayload, error) { +func (r mutationResolver) CloseBug(_ context.Context, input models.CloseBugInput) (*models.CloseBugPayload, error) { repo, err := r.getRepo(input.RepoRef) if err != nil { return nil, err @@ -134,12 +134,12 @@ func (r mutationResolver) CloseBug(ctx context.Context, input models.CloseBugInp return &models.CloseBugPayload{ ClientMutationID: input.ClientMutationID, - Bug: b.Snapshot(), + Bug: models.NewLoadedBug(b.Snapshot()), Operation: op, }, nil } -func (r mutationResolver) SetTitle(ctx context.Context, input models.SetTitleInput) (*models.SetTitlePayload, error) { +func (r mutationResolver) SetTitle(_ context.Context, input models.SetTitleInput) (*models.SetTitlePayload, error) { repo, err := r.getRepo(input.RepoRef) if err != nil { return nil, err @@ -157,12 +157,12 @@ func (r mutationResolver) SetTitle(ctx context.Context, input models.SetTitleInp return &models.SetTitlePayload{ ClientMutationID: input.ClientMutationID, - Bug: b.Snapshot(), + Bug: models.NewLoadedBug(b.Snapshot()), Operation: op, }, nil } -func (r mutationResolver) Commit(ctx context.Context, input models.CommitInput) (*models.CommitPayload, error) { +func (r mutationResolver) Commit(_ context.Context, input models.CommitInput) (*models.CommitPayload, error) { repo, err := r.getRepo(input.RepoRef) if err != nil { return nil, err @@ -180,11 +180,11 @@ func (r mutationResolver) Commit(ctx context.Context, input models.CommitInput) return &models.CommitPayload{ ClientMutationID: input.ClientMutationID, - Bug: b.Snapshot(), + Bug: models.NewLoadedBug(b.Snapshot()), }, nil } -func (r mutationResolver) CommitAsNeeded(ctx context.Context, input models.CommitAsNeededInput) (*models.CommitAsNeededPayload, error) { +func (r mutationResolver) CommitAsNeeded(_ context.Context, input models.CommitAsNeededInput) (*models.CommitAsNeededPayload, error) { repo, err := r.getRepo(input.RepoRef) if err != nil { return nil, err @@ -202,6 +202,6 @@ func (r mutationResolver) CommitAsNeeded(ctx context.Context, input models.Commi return &models.CommitAsNeededPayload{ ClientMutationID: input.ClientMutationID, - Bug: b.Snapshot(), + Bug: models.NewLoadedBug(b.Snapshot()), }, nil } diff --git a/graphql/resolvers/operations.go b/graphql/resolvers/operations.go index ec803c1f..29110cf3 100644 --- a/graphql/resolvers/operations.go +++ b/graphql/resolvers/operations.go @@ -14,11 +14,15 @@ var _ graph.CreateOperationResolver = createOperationResolver{} type createOperationResolver struct{} -func (createOperationResolver) ID(ctx context.Context, obj *bug.CreateOperation) (string, error) { +func (createOperationResolver) ID(_ context.Context, obj *bug.CreateOperation) (string, error) { return obj.Id().String(), nil } -func (createOperationResolver) Date(ctx context.Context, obj *bug.CreateOperation) (*time.Time, error) { +func (createOperationResolver) Author(_ context.Context, obj *bug.CreateOperation) (models.IdentityWrapper, error) { + return models.NewLoadedIdentity(obj.Author), nil +} + +func (createOperationResolver) Date(_ context.Context, obj *bug.CreateOperation) (*time.Time, error) { t := obj.Time() return &t, nil } @@ -27,11 +31,15 @@ var _ graph.AddCommentOperationResolver = addCommentOperationResolver{} type addCommentOperationResolver struct{} -func (addCommentOperationResolver) ID(ctx context.Context, obj *bug.AddCommentOperation) (string, error) { +func (addCommentOperationResolver) ID(_ context.Context, obj *bug.AddCommentOperation) (string, error) { return obj.Id().String(), nil } -func (addCommentOperationResolver) Date(ctx context.Context, obj *bug.AddCommentOperation) (*time.Time, error) { +func (addCommentOperationResolver) Author(_ context.Context, obj *bug.AddCommentOperation) (models.IdentityWrapper, error) { + return models.NewLoadedIdentity(obj.Author), nil +} + +func (addCommentOperationResolver) Date(_ context.Context, obj *bug.AddCommentOperation) (*time.Time, error) { t := obj.Time() return &t, nil } @@ -40,15 +48,19 @@ var _ graph.EditCommentOperationResolver = editCommentOperationResolver{} type editCommentOperationResolver struct{} -func (editCommentOperationResolver) ID(ctx context.Context, obj *bug.EditCommentOperation) (string, error) { +func (editCommentOperationResolver) ID(_ context.Context, obj *bug.EditCommentOperation) (string, error) { return obj.Id().String(), nil } -func (editCommentOperationResolver) Target(ctx context.Context, obj *bug.EditCommentOperation) (string, error) { +func (editCommentOperationResolver) Target(_ context.Context, obj *bug.EditCommentOperation) (string, error) { return obj.Target.String(), nil } -func (editCommentOperationResolver) Date(ctx context.Context, obj *bug.EditCommentOperation) (*time.Time, error) { +func (editCommentOperationResolver) Author(_ context.Context, obj *bug.EditCommentOperation) (models.IdentityWrapper, error) { + return models.NewLoadedIdentity(obj.Author), nil +} + +func (editCommentOperationResolver) Date(_ context.Context, obj *bug.EditCommentOperation) (*time.Time, error) { t := obj.Time() return &t, nil } @@ -57,11 +69,15 @@ var _ graph.LabelChangeOperationResolver = labelChangeOperationResolver{} type labelChangeOperationResolver struct{} -func (labelChangeOperationResolver) ID(ctx context.Context, obj *bug.LabelChangeOperation) (string, error) { +func (labelChangeOperationResolver) ID(_ context.Context, obj *bug.LabelChangeOperation) (string, error) { return obj.Id().String(), nil } -func (labelChangeOperationResolver) Date(ctx context.Context, obj *bug.LabelChangeOperation) (*time.Time, error) { +func (labelChangeOperationResolver) Author(_ context.Context, obj *bug.LabelChangeOperation) (models.IdentityWrapper, error) { + return models.NewLoadedIdentity(obj.Author), nil +} + +func (labelChangeOperationResolver) Date(_ context.Context, obj *bug.LabelChangeOperation) (*time.Time, error) { t := obj.Time() return &t, nil } @@ -70,16 +86,20 @@ var _ graph.SetStatusOperationResolver = setStatusOperationResolver{} type setStatusOperationResolver struct{} -func (setStatusOperationResolver) ID(ctx context.Context, obj *bug.SetStatusOperation) (string, error) { +func (setStatusOperationResolver) ID(_ context.Context, obj *bug.SetStatusOperation) (string, error) { return obj.Id().String(), nil } -func (setStatusOperationResolver) Date(ctx context.Context, obj *bug.SetStatusOperation) (*time.Time, error) { +func (setStatusOperationResolver) Author(_ context.Context, obj *bug.SetStatusOperation) (models.IdentityWrapper, error) { + return models.NewLoadedIdentity(obj.Author), nil +} + +func (setStatusOperationResolver) Date(_ context.Context, obj *bug.SetStatusOperation) (*time.Time, error) { t := obj.Time() return &t, nil } -func (setStatusOperationResolver) Status(ctx context.Context, obj *bug.SetStatusOperation) (models.Status, error) { +func (setStatusOperationResolver) Status(_ context.Context, obj *bug.SetStatusOperation) (models.Status, error) { return convertStatus(obj.Status) } @@ -87,11 +107,15 @@ var _ graph.SetTitleOperationResolver = setTitleOperationResolver{} type setTitleOperationResolver struct{} -func (setTitleOperationResolver) ID(ctx context.Context, obj *bug.SetTitleOperation) (string, error) { +func (setTitleOperationResolver) ID(_ context.Context, obj *bug.SetTitleOperation) (string, error) { return obj.Id().String(), nil } -func (setTitleOperationResolver) Date(ctx context.Context, obj *bug.SetTitleOperation) (*time.Time, error) { +func (setTitleOperationResolver) Author(_ context.Context, obj *bug.SetTitleOperation) (models.IdentityWrapper, error) { + return models.NewLoadedIdentity(obj.Author), nil +} + +func (setTitleOperationResolver) Date(_ context.Context, obj *bug.SetTitleOperation) (*time.Time, error) { t := obj.Time() return &t, nil } diff --git a/graphql/resolvers/query.go b/graphql/resolvers/query.go index aa4e1d85..9503ccf4 100644 --- a/graphql/resolvers/query.go +++ b/graphql/resolvers/query.go @@ -14,7 +14,7 @@ type rootQueryResolver struct { cache *cache.MultiRepoCache } -func (r rootQueryResolver) DefaultRepository(ctx context.Context) (*models.Repository, error) { +func (r rootQueryResolver) DefaultRepository(_ context.Context) (*models.Repository, error) { repo, err := r.cache.DefaultRepo() if err != nil { @@ -27,7 +27,7 @@ func (r rootQueryResolver) DefaultRepository(ctx context.Context) (*models.Repos }, nil } -func (r rootQueryResolver) Repository(ctx context.Context, ref string) (*models.Repository, error) { +func (r rootQueryResolver) Repository(_ context.Context, ref string) (*models.Repository, error) { repo, err := r.cache.ResolveRepo(ref) if err != nil { diff --git a/graphql/resolvers/repo.go b/graphql/resolvers/repo.go index 5a37ae18..3b0aa9a1 100644 --- a/graphql/resolvers/repo.go +++ b/graphql/resolvers/repo.go @@ -9,14 +9,13 @@ import ( "github.com/MichaelMure/git-bug/graphql/connections" "github.com/MichaelMure/git-bug/graphql/graph" "github.com/MichaelMure/git-bug/graphql/models" - "github.com/MichaelMure/git-bug/identity" ) var _ graph.RepositoryResolver = &repoResolver{} type repoResolver struct{} -func (repoResolver) AllBugs(ctx context.Context, obj *models.Repository, after *string, before *string, first *int, last *int, queryStr *string) (*models.BugConnection, error) { +func (repoResolver) AllBugs(_ context.Context, obj *models.Repository, after *string, before *string, first *int, last *int, queryStr *string) (*models.BugConnection, error) { input := models.ConnectionInput{ Before: before, After: after, @@ -49,22 +48,21 @@ 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 []entity.Id, info *models.PageInfo, totalCount int) (*models.BugConnection, error) { edges := make([]*models.BugEdge, len(lazyBugEdges)) - nodes := make([]*bug.Snapshot, len(lazyBugEdges)) + nodes := make([]models.BugWrapper, len(lazyBugEdges)) for i, lazyBugEdge := range lazyBugEdges { - b, err := obj.Repo.ResolveBug(lazyBugEdge.Id) - + excerpt, err := obj.Repo.ResolveBugExcerpt(lazyBugEdge.Id) if err != nil { return nil, err } - snap := b.Snapshot() + b := models.NewLazyBug(obj.Repo, excerpt) edges[i] = &models.BugEdge{ Cursor: lazyBugEdge.Cursor, - Node: snap, + Node: b, } - nodes[i] = snap + nodes[i] = b } return &models.BugConnection{ @@ -78,17 +76,16 @@ func (repoResolver) AllBugs(ctx context.Context, obj *models.Repository, after * return connections.LazyBugCon(source, edger, conMaker, input) } -func (repoResolver) Bug(ctx context.Context, obj *models.Repository, prefix string) (*bug.Snapshot, error) { - b, err := obj.Repo.ResolveBugPrefix(prefix) - +func (repoResolver) Bug(_ context.Context, obj *models.Repository, prefix string) (models.BugWrapper, error) { + excerpt, err := obj.Repo.ResolveBugExcerptPrefix(prefix) if err != nil { return nil, err } - return b.Snapshot(), nil + return models.NewLazyBug(obj.Repo, excerpt), nil } -func (repoResolver) AllIdentities(ctx context.Context, obj *models.Repository, after *string, before *string, first *int, last *int) (*models.IdentityConnection, error) { +func (repoResolver) AllIdentities(_ context.Context, obj *models.Repository, after *string, before *string, first *int, last *int) (*models.IdentityConnection, error) { input := models.ConnectionInput{ Before: before, After: after, @@ -110,22 +107,21 @@ 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 []entity.Id, info *models.PageInfo, totalCount int) (*models.IdentityConnection, error) { edges := make([]*models.IdentityEdge, len(lazyIdentityEdges)) - nodes := make([]identity.Interface, len(lazyIdentityEdges)) + nodes := make([]models.IdentityWrapper, len(lazyIdentityEdges)) for k, lazyIdentityEdge := range lazyIdentityEdges { - i, err := obj.Repo.ResolveIdentity(lazyIdentityEdge.Id) - + excerpt, err := obj.Repo.ResolveIdentityExcerpt(lazyIdentityEdge.Id) if err != nil { return nil, err } - ii := identity.Interface(i.Identity) + i := models.NewLazyIdentity(obj.Repo, excerpt) edges[k] = &models.IdentityEdge{ Cursor: lazyIdentityEdge.Cursor, - Node: i.Identity, + Node: i, } - nodes[k] = ii + nodes[k] = i } return &models.IdentityConnection{ @@ -139,27 +135,25 @@ func (repoResolver) AllIdentities(ctx context.Context, obj *models.Repository, a return connections.LazyIdentityCon(source, edger, conMaker, input) } -func (repoResolver) Identity(ctx context.Context, obj *models.Repository, prefix string) (identity.Interface, error) { - i, err := obj.Repo.ResolveIdentityPrefix(prefix) - +func (repoResolver) Identity(_ context.Context, obj *models.Repository, prefix string) (models.IdentityWrapper, error) { + excerpt, err := obj.Repo.ResolveIdentityExcerptPrefix(prefix) if err != nil { return nil, err } - return i.Identity, nil + return models.NewLazyIdentity(obj.Repo, excerpt), nil } -func (repoResolver) UserIdentity(ctx context.Context, obj *models.Repository) (identity.Interface, error) { - i, err := obj.Repo.GetUserIdentity() - +func (repoResolver) UserIdentity(_ context.Context, obj *models.Repository) (models.IdentityWrapper, error) { + excerpt, err := obj.Repo.GetUserIdentityExcerpt() if err != nil { return nil, err } - return i.Identity, nil + return models.NewLazyIdentity(obj.Repo, excerpt), nil } -func (resolver repoResolver) ValidLabels(ctx context.Context, obj *models.Repository, after *string, before *string, first *int, last *int) (*models.LabelConnection, error) { +func (resolver repoResolver) ValidLabels(_ context.Context, obj *models.Repository, after *string, before *string, first *int, last *int) (*models.LabelConnection, error) { input := models.ConnectionInput{ Before: before, After: after, diff --git a/graphql/resolvers/root.go b/graphql/resolvers/root.go index 3b129f3b..9973ff59 100644 --- a/graphql/resolvers/root.go +++ b/graphql/resolvers/root.go @@ -42,6 +42,10 @@ func (RootResolver) Color() graph.ColorResolver { return &colorResolver{} } +func (r RootResolver) Comment() graph.CommentResolver { + return &commentResolver{} +} + func (RootResolver) Label() graph.LabelResolver { return &labelResolver{} } diff --git a/graphql/resolvers/timeline.go b/graphql/resolvers/timeline.go index b206f898..acf236f8 100644 --- a/graphql/resolvers/timeline.go +++ b/graphql/resolvers/timeline.go @@ -13,7 +13,7 @@ var _ graph.CommentHistoryStepResolver = commentHistoryStepResolver{} type commentHistoryStepResolver struct{} -func (commentHistoryStepResolver) Date(ctx context.Context, obj *bug.CommentHistoryStep) (*time.Time, error) { +func (commentHistoryStepResolver) Date(_ context.Context, obj *bug.CommentHistoryStep) (*time.Time, error) { t := obj.UnixTime.Time() return &t, nil } @@ -22,16 +22,20 @@ var _ graph.AddCommentTimelineItemResolver = addCommentTimelineItemResolver{} type addCommentTimelineItemResolver struct{} -func (addCommentTimelineItemResolver) ID(ctx context.Context, obj *bug.AddCommentTimelineItem) (string, error) { +func (addCommentTimelineItemResolver) ID(_ context.Context, obj *bug.AddCommentTimelineItem) (string, error) { return obj.Id().String(), nil } -func (addCommentTimelineItemResolver) CreatedAt(ctx context.Context, obj *bug.AddCommentTimelineItem) (*time.Time, error) { +func (addCommentTimelineItemResolver) Author(_ context.Context, obj *bug.AddCommentTimelineItem) (models.IdentityWrapper, error) { + return models.NewLoadedIdentity(obj.Author), nil +} + +func (addCommentTimelineItemResolver) CreatedAt(_ context.Context, obj *bug.AddCommentTimelineItem) (*time.Time, error) { t := obj.CreatedAt.Time() return &t, nil } -func (addCommentTimelineItemResolver) LastEdit(ctx context.Context, obj *bug.AddCommentTimelineItem) (*time.Time, error) { +func (addCommentTimelineItemResolver) LastEdit(_ context.Context, obj *bug.AddCommentTimelineItem) (*time.Time, error) { t := obj.LastEdit.Time() return &t, nil } @@ -40,16 +44,20 @@ var _ graph.CreateTimelineItemResolver = createTimelineItemResolver{} type createTimelineItemResolver struct{} -func (createTimelineItemResolver) ID(ctx context.Context, obj *bug.CreateTimelineItem) (string, error) { +func (createTimelineItemResolver) ID(_ context.Context, obj *bug.CreateTimelineItem) (string, error) { return obj.Id().String(), nil } -func (createTimelineItemResolver) CreatedAt(ctx context.Context, obj *bug.CreateTimelineItem) (*time.Time, error) { +func (r createTimelineItemResolver) Author(_ context.Context, obj *bug.CreateTimelineItem) (models.IdentityWrapper, error) { + return models.NewLoadedIdentity(obj.Author), nil +} + +func (createTimelineItemResolver) CreatedAt(_ context.Context, obj *bug.CreateTimelineItem) (*time.Time, error) { t := obj.CreatedAt.Time() return &t, nil } -func (createTimelineItemResolver) LastEdit(ctx context.Context, obj *bug.CreateTimelineItem) (*time.Time, error) { +func (createTimelineItemResolver) LastEdit(_ context.Context, obj *bug.CreateTimelineItem) (*time.Time, error) { t := obj.LastEdit.Time() return &t, nil } @@ -58,11 +66,15 @@ var _ graph.LabelChangeTimelineItemResolver = labelChangeTimelineItem{} type labelChangeTimelineItem struct{} -func (labelChangeTimelineItem) ID(ctx context.Context, obj *bug.LabelChangeTimelineItem) (string, error) { +func (labelChangeTimelineItem) ID(_ context.Context, obj *bug.LabelChangeTimelineItem) (string, error) { return obj.Id().String(), nil } -func (labelChangeTimelineItem) Date(ctx context.Context, obj *bug.LabelChangeTimelineItem) (*time.Time, error) { +func (i labelChangeTimelineItem) Author(_ context.Context, obj *bug.LabelChangeTimelineItem) (models.IdentityWrapper, error) { + return models.NewLoadedIdentity(obj.Author), nil +} + +func (labelChangeTimelineItem) Date(_ context.Context, obj *bug.LabelChangeTimelineItem) (*time.Time, error) { t := obj.UnixTime.Time() return &t, nil } @@ -71,16 +83,20 @@ var _ graph.SetStatusTimelineItemResolver = setStatusTimelineItem{} type setStatusTimelineItem struct{} -func (setStatusTimelineItem) ID(ctx context.Context, obj *bug.SetStatusTimelineItem) (string, error) { +func (setStatusTimelineItem) ID(_ context.Context, obj *bug.SetStatusTimelineItem) (string, error) { return obj.Id().String(), nil } -func (setStatusTimelineItem) Date(ctx context.Context, obj *bug.SetStatusTimelineItem) (*time.Time, error) { +func (i setStatusTimelineItem) Author(_ context.Context, obj *bug.SetStatusTimelineItem) (models.IdentityWrapper, error) { + return models.NewLoadedIdentity(obj.Author), nil +} + +func (setStatusTimelineItem) Date(_ context.Context, obj *bug.SetStatusTimelineItem) (*time.Time, error) { t := obj.UnixTime.Time() return &t, nil } -func (setStatusTimelineItem) Status(ctx context.Context, obj *bug.SetStatusTimelineItem) (models.Status, error) { +func (setStatusTimelineItem) Status(_ context.Context, obj *bug.SetStatusTimelineItem) (models.Status, error) { return convertStatus(obj.Status) } @@ -88,11 +104,15 @@ var _ graph.SetTitleTimelineItemResolver = setTitleTimelineItem{} type setTitleTimelineItem struct{} -func (setTitleTimelineItem) ID(ctx context.Context, obj *bug.SetTitleTimelineItem) (string, error) { +func (setTitleTimelineItem) ID(_ context.Context, obj *bug.SetTitleTimelineItem) (string, error) { return obj.Id().String(), nil } -func (setTitleTimelineItem) Date(ctx context.Context, obj *bug.SetTitleTimelineItem) (*time.Time, error) { +func (i setTitleTimelineItem) Author(_ context.Context, obj *bug.SetTitleTimelineItem) (models.IdentityWrapper, error) { + return models.NewLoadedIdentity(obj.Author), nil +} + +func (setTitleTimelineItem) Date(_ context.Context, obj *bug.SetTitleTimelineItem) (*time.Time, error) { t := obj.UnixTime.Time() return &t, nil } diff --git a/identity/identity.go b/identity/identity.go index c33a8818..b5222a3e 100644 --- a/identity/identity.go +++ b/identity/identity.go @@ -227,17 +227,35 @@ func SetUserIdentity(repo repository.RepoConfig, identity *Identity) error { // GetUserIdentity read the current user identity, set with a git config entry func GetUserIdentity(repo repository.Repo) (*Identity, error) { - configs, err := repo.LocalConfig().ReadAll(identityConfigKey) + id, err := GetUserIdentityId(repo) if err != nil { return nil, err } + i, err := ReadLocal(repo, id) + if err == ErrIdentityNotExist { + innerErr := repo.LocalConfig().RemoveAll(identityConfigKey) + if innerErr != nil { + _, _ = fmt.Fprintln(os.Stderr, errors.Wrap(innerErr, "can't clear user identity").Error()) + } + return nil, err + } + + return i, nil +} + +func GetUserIdentityId(repo repository.Repo) (entity.Id, error) { + configs, err := repo.LocalConfig().ReadAll(identityConfigKey) + if err != nil { + return entity.UnsetId, err + } + if len(configs) == 0 { - return nil, ErrNoIdentitySet + return entity.UnsetId, ErrNoIdentitySet } if len(configs) > 1 { - return nil, ErrMultipleIdentitiesSet + return entity.UnsetId, ErrMultipleIdentitiesSet } var id entity.Id @@ -246,19 +264,10 @@ func GetUserIdentity(repo repository.Repo) (*Identity, error) { } if err := id.Validate(); err != nil { - return nil, err - } - - i, err := ReadLocal(repo, id) - if err == ErrIdentityNotExist { - innerErr := repo.LocalConfig().RemoveAll(identityConfigKey) - if innerErr != nil { - _, _ = fmt.Fprintln(os.Stderr, errors.Wrap(innerErr, "can't clear user identity").Error()) - } - return nil, err + return entity.UnsetId, err } - return i, nil + return id, nil } // IsUserIdentitySet say if the user has set his identity |