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