1package bug
2
3import (
4 "encoding/json"
5 "fmt"
6
7 "github.com/MichaelMure/git-bug/identity"
8 "github.com/MichaelMure/git-bug/util/git"
9 "github.com/MichaelMure/git-bug/util/text"
10 "github.com/MichaelMure/git-bug/util/timestamp"
11)
12
13var _ Operation = &AddCommentOperation{}
14
15// AddCommentOperation will add a new comment in the bug
16type AddCommentOperation struct {
17 OpBase
18 Message string
19 // TODO: change for a map[string]util.hash to store the filename ?
20 Files []git.Hash
21}
22
23func (op *AddCommentOperation) base() *OpBase {
24 return &op.OpBase
25}
26
27func (op *AddCommentOperation) Hash() (git.Hash, error) {
28 return hashOperation(op)
29}
30
31func (op *AddCommentOperation) Apply(snapshot *Snapshot) {
32 snapshot.addActor(op.Author)
33 snapshot.addParticipant(op.Author)
34
35 hash, err := op.Hash()
36 if err != nil {
37 // Should never error unless a programming error happened
38 // (covered in OpBase.Validate())
39 panic(err)
40 }
41
42 comment := Comment{
43 id: string(hash),
44 Message: op.Message,
45 Author: op.Author,
46 Files: op.Files,
47 UnixTime: timestamp.Timestamp(op.UnixTime),
48 }
49
50 snapshot.Comments = append(snapshot.Comments, comment)
51
52 item := &AddCommentTimelineItem{
53 CommentTimelineItem: NewCommentTimelineItem(hash, comment),
54 }
55
56 snapshot.Timeline = append(snapshot.Timeline, item)
57}
58
59func (op *AddCommentOperation) GetFiles() []git.Hash {
60 return op.Files
61}
62
63func (op *AddCommentOperation) Validate() error {
64 if err := opBaseValidate(op, AddCommentOp); err != nil {
65 return err
66 }
67
68 if !text.Safe(op.Message) {
69 return fmt.Errorf("message is not fully printable")
70 }
71
72 return nil
73}
74
75// Workaround to avoid the inner OpBase.MarshalJSON overriding the outer op
76// MarshalJSON
77func (op *AddCommentOperation) MarshalJSON() ([]byte, error) {
78 base, err := json.Marshal(op.OpBase)
79 if err != nil {
80 return nil, err
81 }
82
83 // revert back to a flat map to be able to add our own fields
84 var data map[string]interface{}
85 if err := json.Unmarshal(base, &data); err != nil {
86 return nil, err
87 }
88
89 data["message"] = op.Message
90 data["files"] = op.Files
91
92 return json.Marshal(data)
93}
94
95// Workaround to avoid the inner OpBase.MarshalJSON overriding the outer op
96// MarshalJSON
97func (op *AddCommentOperation) UnmarshalJSON(data []byte) error {
98 // Unmarshal OpBase and the op separately
99
100 base := OpBase{}
101 err := json.Unmarshal(data, &base)
102 if err != nil {
103 return err
104 }
105
106 aux := struct {
107 Message string `json:"message"`
108 Files []git.Hash `json:"files"`
109 }{}
110
111 err = json.Unmarshal(data, &aux)
112 if err != nil {
113 return err
114 }
115
116 op.OpBase = base
117 op.Message = aux.Message
118 op.Files = aux.Files
119
120 return nil
121}
122
123// Sign post method for gqlgen
124func (op *AddCommentOperation) IsAuthored() {}
125
126func NewAddCommentOp(author identity.Interface, unixTime int64, message string, files []git.Hash) *AddCommentOperation {
127 return &AddCommentOperation{
128 OpBase: newOpBase(AddCommentOp, author, unixTime),
129 Message: message,
130 Files: files,
131 }
132}
133
134// CreateTimelineItem replace a AddComment operation in the Timeline and hold its edition history
135type AddCommentTimelineItem struct {
136 CommentTimelineItem
137}
138
139// Convenience function to apply the operation
140func AddComment(b Interface, author identity.Interface, unixTime int64, message string) (*AddCommentOperation, error) {
141 return AddCommentWithFiles(b, author, unixTime, message, nil)
142}
143
144func AddCommentWithFiles(b Interface, author identity.Interface, unixTime int64, message string, files []git.Hash) (*AddCommentOperation, error) {
145 addCommentOp := NewAddCommentOp(author, unixTime, message, files)
146 if err := addCommentOp.Validate(); err != nil {
147 return nil, err
148 }
149 b.Append(addCommentOp)
150 return addCommentOp, nil
151}