lazy_bug.go

  1package models
  2
  3import (
  4	"sync"
  5	"time"
  6
  7	"github.com/MichaelMure/git-bug/bug"
  8	"github.com/MichaelMure/git-bug/cache"
  9	"github.com/MichaelMure/git-bug/entity"
 10)
 11
 12// BugWrapper is an interface used by the GraphQL resolvers to handle a bug.
 13// Depending on the situation, a Bug can already be fully loaded in memory or not.
 14// This interface is used to wrap either a lazyBug or a loadedBug depending on the situation.
 15type BugWrapper interface {
 16	Id() entity.Id
 17	LastEdit() time.Time
 18	Status() bug.Status
 19	Title() string
 20	Comments() ([]bug.Comment, error)
 21	Labels() []bug.Label
 22	Author() (IdentityWrapper, error)
 23	Actors() ([]IdentityWrapper, error)
 24	Participants() ([]IdentityWrapper, error)
 25	CreatedAt() time.Time
 26	Timeline() ([]bug.TimelineItem, error)
 27	Operations() ([]bug.Operation, error)
 28
 29	IsAuthored()
 30}
 31
 32var _ BugWrapper = &lazyBug{}
 33
 34// lazyBug is a lazy-loading wrapper that fetch data from the cache (BugExcerpt) in priority,
 35// and load the complete bug and snapshot only when necessary.
 36type lazyBug struct {
 37	cache   *cache.RepoCache
 38	excerpt *cache.BugExcerpt
 39
 40	mu   sync.Mutex
 41	snap *bug.Snapshot
 42}
 43
 44func NewLazyBug(cache *cache.RepoCache, excerpt *cache.BugExcerpt) *lazyBug {
 45	return &lazyBug{
 46		cache:   cache,
 47		excerpt: excerpt,
 48	}
 49}
 50
 51func (lb *lazyBug) load() error {
 52	lb.mu.Lock()
 53	defer lb.mu.Unlock()
 54
 55	if lb.snap != nil {
 56		return nil
 57	}
 58
 59	b, err := lb.cache.ResolveBug(lb.excerpt.Id)
 60	if err != nil {
 61		return err
 62	}
 63
 64	lb.snap = b.Snapshot()
 65	return nil
 66}
 67
 68func (lb *lazyBug) identity(id entity.Id) (IdentityWrapper, error) {
 69	i, err := lb.cache.ResolveIdentityExcerpt(id)
 70	if err != nil {
 71		return nil, err
 72	}
 73	return &lazyIdentity{cache: lb.cache, excerpt: i}, nil
 74}
 75
 76// Sign post method for gqlgen
 77func (lb *lazyBug) IsAuthored() {}
 78
 79func (lb *lazyBug) Id() entity.Id {
 80	return lb.excerpt.Id
 81}
 82
 83func (lb *lazyBug) LastEdit() time.Time {
 84	return lb.excerpt.EditTime()
 85}
 86
 87func (lb *lazyBug) Status() bug.Status {
 88	return lb.excerpt.Status
 89}
 90
 91func (lb *lazyBug) Title() string {
 92	return lb.excerpt.Title
 93}
 94
 95func (lb *lazyBug) Comments() ([]bug.Comment, error) {
 96	err := lb.load()
 97	if err != nil {
 98		return nil, err
 99	}
100	return lb.snap.Comments, nil
101}
102
103func (lb *lazyBug) Labels() []bug.Label {
104	return lb.excerpt.Labels
105}
106
107func (lb *lazyBug) Author() (IdentityWrapper, error) {
108	return lb.identity(lb.excerpt.AuthorId)
109}
110
111func (lb *lazyBug) Actors() ([]IdentityWrapper, error) {
112	result := make([]IdentityWrapper, len(lb.excerpt.Actors))
113	for i, actorId := range lb.excerpt.Actors {
114		actor, err := lb.identity(actorId)
115		if err != nil {
116			return nil, err
117		}
118		result[i] = actor
119	}
120	return result, nil
121}
122
123func (lb *lazyBug) Participants() ([]IdentityWrapper, error) {
124	result := make([]IdentityWrapper, len(lb.excerpt.Participants))
125	for i, participantId := range lb.excerpt.Participants {
126		participant, err := lb.identity(participantId)
127		if err != nil {
128			return nil, err
129		}
130		result[i] = participant
131	}
132	return result, nil
133}
134
135func (lb *lazyBug) CreatedAt() time.Time {
136	return lb.excerpt.CreateTime()
137}
138
139func (lb *lazyBug) Timeline() ([]bug.TimelineItem, error) {
140	err := lb.load()
141	if err != nil {
142		return nil, err
143	}
144	return lb.snap.Timeline, nil
145}
146
147func (lb *lazyBug) Operations() ([]bug.Operation, error) {
148	err := lb.load()
149	if err != nil {
150		return nil, err
151	}
152	return lb.snap.Operations, nil
153}
154
155var _ BugWrapper = &loadedBug{}
156
157type loadedBug struct {
158	*bug.Snapshot
159}
160
161func NewLoadedBug(snap *bug.Snapshot) *loadedBug {
162	return &loadedBug{Snapshot: snap}
163}
164
165func (l *loadedBug) LastEdit() time.Time {
166	return l.Snapshot.EditTime()
167}
168
169func (l *loadedBug) Status() bug.Status {
170	return l.Snapshot.Status
171}
172
173func (l *loadedBug) Title() string {
174	return l.Snapshot.Title
175}
176
177func (l *loadedBug) Comments() ([]bug.Comment, error) {
178	return l.Snapshot.Comments, nil
179}
180
181func (l *loadedBug) Labels() []bug.Label {
182	return l.Snapshot.Labels
183}
184
185func (l *loadedBug) Author() (IdentityWrapper, error) {
186	return NewLoadedIdentity(l.Snapshot.Author), nil
187}
188
189func (l *loadedBug) Actors() ([]IdentityWrapper, error) {
190	res := make([]IdentityWrapper, len(l.Snapshot.Actors))
191	for i, actor := range l.Snapshot.Actors {
192		res[i] = NewLoadedIdentity(actor)
193	}
194	return res, nil
195}
196
197func (l *loadedBug) Participants() ([]IdentityWrapper, error) {
198	res := make([]IdentityWrapper, len(l.Snapshot.Participants))
199	for i, participant := range l.Snapshot.Participants {
200		res[i] = NewLoadedIdentity(participant)
201	}
202	return res, nil
203}
204
205func (l *loadedBug) CreatedAt() time.Time {
206	return l.Snapshot.CreateTime
207}
208
209func (l *loadedBug) Timeline() ([]bug.TimelineItem, error) {
210	return l.Snapshot.Timeline, nil
211}
212
213func (l *loadedBug) Operations() ([]bug.Operation, error) {
214	return l.Snapshot.Operations, nil
215}