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