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) ID() string {
28 return idOperation(op)
29}
30
31func (op *AddCommentOperation) Apply(snapshot *Snapshot) {
32 snapshot.addActor(op.Author)
33 snapshot.addParticipant(op.Author)
34
35 comment := Comment{
36 id: op.ID(),
37 Message: op.Message,
38 Author: op.Author,
39 Files: op.Files,
40 UnixTime: timestamp.Timestamp(op.UnixTime),
41 }
42
43 snapshot.Comments = append(snapshot.Comments, comment)
44
45 item := &AddCommentTimelineItem{
46 CommentTimelineItem: NewCommentTimelineItem(op.ID(), comment),
47 }
48
49 snapshot.Timeline = append(snapshot.Timeline, item)
50}
51
52func (op *AddCommentOperation) GetFiles() []git.Hash {
53 return op.Files
54}
55
56func (op *AddCommentOperation) Validate() error {
57 if err := opBaseValidate(op, AddCommentOp); err != nil {
58 return err
59 }
60
61 if !text.Safe(op.Message) {
62 return fmt.Errorf("message is not fully printable")
63 }
64
65 return nil
66}
67
68// Workaround to avoid the inner OpBase.MarshalJSON overriding the outer op
69// MarshalJSON
70func (op *AddCommentOperation) MarshalJSON() ([]byte, error) {
71 base, err := json.Marshal(op.OpBase)
72 if err != nil {
73 return nil, err
74 }
75
76 // revert back to a flat map to be able to add our own fields
77 var data map[string]interface{}
78 if err := json.Unmarshal(base, &data); err != nil {
79 return nil, err
80 }
81
82 data["message"] = op.Message
83 data["files"] = op.Files
84
85 return json.Marshal(data)
86}
87
88// Workaround to avoid the inner OpBase.MarshalJSON overriding the outer op
89// MarshalJSON
90func (op *AddCommentOperation) UnmarshalJSON(data []byte) error {
91 // Unmarshal OpBase and the op separately
92
93 base := OpBase{}
94 err := json.Unmarshal(data, &base)
95 if err != nil {
96 return err
97 }
98
99 aux := struct {
100 Message string `json:"message"`
101 Files []git.Hash `json:"files"`
102 }{}
103
104 err = json.Unmarshal(data, &aux)
105 if err != nil {
106 return err
107 }
108
109 op.OpBase = base
110 op.Message = aux.Message
111 op.Files = aux.Files
112
113 return nil
114}
115
116// Sign post method for gqlgen
117func (op *AddCommentOperation) IsAuthored() {}
118
119func NewAddCommentOp(author identity.Interface, unixTime int64, message string, files []git.Hash) *AddCommentOperation {
120 return &AddCommentOperation{
121 OpBase: newOpBase(AddCommentOp, author, unixTime),
122 Message: message,
123 Files: files,
124 }
125}
126
127// CreateTimelineItem replace a AddComment operation in the Timeline and hold its edition history
128type AddCommentTimelineItem struct {
129 CommentTimelineItem
130}
131
132// Sign post method for gqlgen
133func (a *AddCommentTimelineItem) IsAuthored() {}
134
135// Convenience function to apply the operation
136func AddComment(b Interface, author identity.Interface, unixTime int64, message string) (*AddCommentOperation, error) {
137 return AddCommentWithFiles(b, author, unixTime, message, nil)
138}
139
140func AddCommentWithFiles(b Interface, author identity.Interface, unixTime int64, message string, files []git.Hash) (*AddCommentOperation, error) {
141 addCommentOp := NewAddCommentOp(author, unixTime, message, files)
142 if err := addCommentOp.Validate(); err != nil {
143 return nil, err
144 }
145 b.Append(addCommentOp)
146 return addCommentOp, nil
147}