1package bug
2
3import (
4 "encoding/json"
5 "fmt"
6
7 "github.com/pkg/errors"
8
9 "github.com/MichaelMure/git-bug/entity"
10 "github.com/MichaelMure/git-bug/identity"
11 "github.com/MichaelMure/git-bug/repository"
12 "github.com/MichaelMure/git-bug/util/timestamp"
13
14 "github.com/MichaelMure/git-bug/util/text"
15)
16
17var _ Operation = &EditCommentOperation{}
18
19// EditCommentOperation will change a comment in the bug
20type EditCommentOperation struct {
21 OpBase
22 Target entity.Id `json:"target"`
23 Message string `json:"message"`
24 Files []repository.Hash `json:"files"`
25}
26
27func (op *EditCommentOperation) Id() entity.Id {
28 return idOperation(op, &op.OpBase)
29}
30
31func (op *EditCommentOperation) Apply(snapshot *Snapshot) {
32 // Todo: currently any message can be edited, even by a different author
33 // crypto signature are needed.
34
35 snapshot.addActor(op.Author_)
36
37 var target TimelineItem
38
39 for i, item := range snapshot.Timeline {
40 if item.Id() == op.Target {
41 target = snapshot.Timeline[i]
42 break
43 }
44 }
45
46 if target == nil {
47 // Target not found, edit is a no-op
48 return
49 }
50
51 comment := Comment{
52 id: op.Target,
53 Message: op.Message,
54 Files: op.Files,
55 UnixTime: timestamp.Timestamp(op.UnixTime),
56 }
57
58 switch target := target.(type) {
59 case *CreateTimelineItem:
60 target.Append(comment)
61 case *AddCommentTimelineItem:
62 target.Append(comment)
63 }
64
65 // Updating the corresponding comment
66
67 for i := range snapshot.Comments {
68 if snapshot.Comments[i].Id() == op.Target {
69 snapshot.Comments[i].Message = op.Message
70 snapshot.Comments[i].Files = op.Files
71 break
72 }
73 }
74}
75
76func (op *EditCommentOperation) GetFiles() []repository.Hash {
77 return op.Files
78}
79
80func (op *EditCommentOperation) Validate() error {
81 if err := op.OpBase.Validate(op, EditCommentOp); err != nil {
82 return err
83 }
84
85 if err := op.Target.Validate(); err != nil {
86 return errors.Wrap(err, "target hash is invalid")
87 }
88
89 if !text.Safe(op.Message) {
90 return fmt.Errorf("message is not fully printable")
91 }
92
93 return nil
94}
95
96// UnmarshalJSON is a two step JSON unmarshalling
97// This workaround is necessary to avoid the inner OpBase.MarshalJSON
98// overriding the outer op's MarshalJSON
99func (op *EditCommentOperation) UnmarshalJSON(data []byte) error {
100 // Unmarshal OpBase and the op separately
101
102 base := OpBase{}
103 err := json.Unmarshal(data, &base)
104 if err != nil {
105 return err
106 }
107
108 aux := struct {
109 Target entity.Id `json:"target"`
110 Message string `json:"message"`
111 Files []repository.Hash `json:"files"`
112 }{}
113
114 err = json.Unmarshal(data, &aux)
115 if err != nil {
116 return err
117 }
118
119 op.OpBase = base
120 op.Target = aux.Target
121 op.Message = aux.Message
122 op.Files = aux.Files
123
124 return nil
125}
126
127// Sign post method for gqlgen
128func (op *EditCommentOperation) IsAuthored() {}
129
130func NewEditCommentOp(author identity.Interface, unixTime int64, target entity.Id, message string, files []repository.Hash) *EditCommentOperation {
131 return &EditCommentOperation{
132 OpBase: newOpBase(EditCommentOp, author, unixTime),
133 Target: target,
134 Message: message,
135 Files: files,
136 }
137}
138
139// Convenience function to apply the operation
140func EditComment(b Interface, author identity.Interface, unixTime int64, target entity.Id, message string) (*EditCommentOperation, error) {
141 return EditCommentWithFiles(b, author, unixTime, target, message, nil)
142}
143
144func EditCommentWithFiles(b Interface, author identity.Interface, unixTime int64, target entity.Id, message string, files []repository.Hash) (*EditCommentOperation, error) {
145 editCommentOp := NewEditCommentOp(author, unixTime, target, message, files)
146 if err := editCommentOp.Validate(); err != nil {
147 return nil, err
148 }
149 b.Append(editCommentOp)
150 return editCommentOp, nil
151}
152
153// Convenience function to edit the body of a bug (the first comment)
154func EditCreateComment(b Interface, author identity.Interface, unixTime int64, message string) (*EditCommentOperation, error) {
155 createOp := b.FirstOp().(*CreateOperation)
156 return EditComment(b, author, unixTime, createOp.Id(), message)
157}
158
159// Convenience function to edit the body of a bug (the first comment)
160func EditCreateCommentWithFiles(b Interface, author identity.Interface, unixTime int64, message string, files []repository.Hash) (*EditCommentOperation, error) {
161 createOp := b.FirstOp().(*CreateOperation)
162 return EditCommentWithFiles(b, author, unixTime, createOp.Id(), message, files)
163}