1package models
2
3import (
4 "sync"
5 "time"
6
7 "github.com/git-bug/git-bug/cache"
8 "github.com/git-bug/git-bug/entities/bug"
9 "github.com/git-bug/git-bug/entities/common"
10 "github.com/git-bug/git-bug/entity"
11 "github.com/git-bug/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() []common.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 fetches 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.Bugs().Resolve(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.Identities().ResolveExcerpt(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() []common.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() []common.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}