1package resolvers
2
3import (
4 "context"
5 "crypto/rand"
6 "encoding/base64"
7 "fmt"
8 "sync"
9
10 "github.com/MichaelMure/git-bug/bug"
11 "github.com/MichaelMure/git-bug/cache"
12 "github.com/MichaelMure/git-bug/graphql/graph"
13 "github.com/MichaelMure/git-bug/graphql/models"
14)
15
16const transactionIdLength = 16
17
18var _ graph.MutationResolver = &mutationResolver{}
19
20type mutationResolver struct {
21 cache *cache.MultiRepoCache
22
23 mu sync.Mutex
24 transactions map[string]transaction
25}
26
27type transaction struct {
28 bug *cache.BugCache
29 ops []bug.Operation
30}
31
32func (r mutationResolver) makeId() (string, error) {
33 result := make([]byte, transactionIdLength)
34
35 i := 0
36 for {
37 _, err := rand.Read(result)
38 if err != nil {
39 panic(err)
40 }
41 id := base64.StdEncoding.EncodeToString(result)
42 if _, has := r.transactions[id]; !has {
43 return id, nil
44 }
45 i++
46 if i > 100 {
47 return "", fmt.Errorf("can't generate a transaction ID")
48 }
49 }
50}
51
52func (r mutationResolver) StartTransaction(_ context.Context, input models.StartTransactionInput) (*models.StartTransactionPayload, error) {
53 r.mu.Lock()
54 defer r.mu.Unlock()
55
56 b, err := r.getBug(input.RepoRef, input.Prefix)
57 if err != nil {
58 return nil, err
59 }
60
61 id, err := r.makeId()
62 if err != nil {
63 return nil, err
64 }
65
66 r.transactions[id] = transaction{
67 bug: b,
68 ops: make([]bug.Operation, 0, 8),
69 }
70
71 return &models.StartTransactionPayload{
72 ClientMutationID: input.ClientMutationID,
73 ID: id,
74 }, nil
75}
76
77func (r mutationResolver) Commit(_ context.Context, input models.CommitInput) (*models.CommitPayload, error) {
78 panic("implement me")
79}
80
81func (r mutationResolver) Rollback(_ context.Context, input models.RollbackInput) (*models.RollbackPayload, error) {
82 panic("implement me")
83}
84
85func (r mutationResolver) getRepo(ref *string) (*cache.RepoCache, error) {
86 if ref != nil {
87 return r.cache.ResolveRepo(*ref)
88 }
89
90 return r.cache.DefaultRepo()
91}
92
93func (r mutationResolver) getBug(repoRef *string, bugPrefix string) (*cache.BugCache, error) {
94 repo, err := r.getRepo(repoRef)
95 if err != nil {
96 return nil, err
97 }
98
99 return repo.ResolveBugPrefix(bugPrefix)
100}
101
102func (r mutationResolver) NewBug(_ context.Context, input models.NewBugInput, txID *string) (*models.NewBugPayload, error) {
103 repo, err := r.getRepo(input.RepoRef)
104 if err != nil {
105 return nil, err
106 }
107
108 b, op, err := repo.NewBugWithFiles(input.Title, input.Message, input.Files)
109 if err != nil {
110 return nil, err
111 }
112
113 return &models.NewBugPayload{
114 ClientMutationID: input.ClientMutationID,
115 Bug: models.NewLoadedBug(b.Snapshot()),
116 Operation: op,
117 }, nil
118}
119
120func (r mutationResolver) AddComment(_ context.Context, input models.AddCommentInput, txID *string) (*models.AddCommentPayload, error) {
121 b, err := r.getBug(input.RepoRef, input.Prefix)
122 if err != nil {
123 return nil, err
124 }
125
126 var op *bug.AddCommentOperation
127 if txID == nil {
128 op, err = b.AddCommentWithFiles(input.Message, input.Files)
129 if err != nil {
130 return nil, err
131 }
132
133 err = b.Commit()
134 if err != nil {
135 return nil, err
136 }
137 } else {
138 op, err = bug.OpAddCommentWithFiles(input.Message)
139 }
140
141 return &models.AddCommentPayload{
142 ClientMutationID: input.ClientMutationID,
143 Bug: models.NewLoadedBug(b.Snapshot()),
144 Operation: op,
145 }, nil
146}
147
148func (r mutationResolver) ChangeLabels(_ context.Context, input models.ChangeLabelInput, txID *string) (*models.ChangeLabelPayload, error) {
149 b, err := r.getBug(input.RepoRef, input.Prefix)
150 if err != nil {
151 return nil, err
152 }
153
154 results, op, err := b.ChangeLabels(input.Added, input.Removed)
155 if err != nil {
156 return nil, err
157 }
158
159 err = b.Commit()
160 if err != nil {
161 return nil, err
162 }
163
164 resultsPtr := make([]*bug.LabelChangeResult, len(results))
165 for i, result := range results {
166 resultsPtr[i] = &result
167 }
168
169 return &models.ChangeLabelPayload{
170 ClientMutationID: input.ClientMutationID,
171 Bug: models.NewLoadedBug(b.Snapshot()),
172 Operation: op,
173 Results: resultsPtr,
174 }, nil
175}
176
177func (r mutationResolver) OpenBug(_ context.Context, input models.OpenBugInput, txID *string) (*models.OpenBugPayload, error) {
178 b, err := r.getBug(input.RepoRef, input.Prefix)
179 if err != nil {
180 return nil, err
181 }
182
183 op, err := b.Open()
184 if err != nil {
185 return nil, err
186 }
187
188 err = b.Commit()
189 if err != nil {
190 return nil, err
191 }
192
193 return &models.OpenBugPayload{
194 ClientMutationID: input.ClientMutationID,
195 Bug: models.NewLoadedBug(b.Snapshot()),
196 Operation: op,
197 }, nil
198}
199
200func (r mutationResolver) CloseBug(_ context.Context, input models.CloseBugInput, txID *string) (*models.CloseBugPayload, error) {
201 b, err := r.getBug(input.RepoRef, input.Prefix)
202 if err != nil {
203 return nil, err
204 }
205
206 op, err := b.Close()
207 if err != nil {
208 return nil, err
209 }
210
211 err = b.Commit()
212 if err != nil {
213 return nil, err
214 }
215
216 return &models.CloseBugPayload{
217 ClientMutationID: input.ClientMutationID,
218 Bug: models.NewLoadedBug(b.Snapshot()),
219 Operation: op,
220 }, nil
221}
222
223func (r mutationResolver) SetTitle(_ context.Context, input models.SetTitleInput, txID *string) (*models.SetTitlePayload, error) {
224 b, err := r.getBug(input.RepoRef, input.Prefix)
225 if err != nil {
226 return nil, err
227 }
228
229 op, err := b.SetTitle(input.Title)
230 if err != nil {
231 return nil, err
232 }
233
234 err = b.Commit()
235 if err != nil {
236 return nil, err
237 }
238
239 return &models.SetTitlePayload{
240 ClientMutationID: input.ClientMutationID,
241 Bug: models.NewLoadedBug(b.Snapshot()),
242 Operation: op,
243 }, nil
244}