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}