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}