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 if lb.snap != nil {
53 return nil
54 }
55
56 lb.mu.Lock()
57 defer lb.mu.Unlock()
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}