op_edit_comment.go

  1package bug
  2
  3import (
  4	"encoding/json"
  5	"fmt"
  6
  7	"github.com/MichaelMure/git-bug/identity"
  8
  9	"github.com/MichaelMure/git-bug/util/git"
 10	"github.com/MichaelMure/git-bug/util/text"
 11)
 12
 13var _ Operation = &EditCommentOperation{}
 14
 15// EditCommentOperation will change a comment in the bug
 16type EditCommentOperation struct {
 17	OpBase
 18	Target  git.Hash
 19	Message string
 20	Files   []git.Hash
 21}
 22
 23func (op *EditCommentOperation) base() *OpBase {
 24	return &op.OpBase
 25}
 26
 27func (op *EditCommentOperation) Hash() (git.Hash, error) {
 28	return hashOperation(op)
 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	var target TimelineItem
 36	var commentIndex int
 37
 38	for i, item := range snapshot.Timeline {
 39		h := item.Hash()
 40
 41		if h == op.Target {
 42			target = snapshot.Timeline[i]
 43			break
 44		}
 45
 46		// Track the index in the []Comment
 47		switch item.(type) {
 48		case *CreateTimelineItem, *CommentTimelineItem:
 49			commentIndex++
 50		}
 51	}
 52
 53	if target == nil {
 54		// Target not found, edit is a no-op
 55		return
 56	}
 57
 58	comment := Comment{
 59		Message:  op.Message,
 60		Files:    op.Files,
 61		UnixTime: Timestamp(op.UnixTime),
 62	}
 63
 64	switch target.(type) {
 65	case *CreateTimelineItem:
 66		item := target.(*CreateTimelineItem)
 67		item.Append(comment)
 68
 69	case *AddCommentTimelineItem:
 70		item := target.(*AddCommentTimelineItem)
 71		item.Append(comment)
 72	}
 73
 74	snapshot.Comments[commentIndex].Message = op.Message
 75	snapshot.Comments[commentIndex].Files = op.Files
 76}
 77
 78func (op *EditCommentOperation) GetFiles() []git.Hash {
 79	return op.Files
 80}
 81
 82func (op *EditCommentOperation) Validate() error {
 83	if err := opBaseValidate(op, EditCommentOp); err != nil {
 84		return err
 85	}
 86
 87	if !op.Target.IsValid() {
 88		return fmt.Errorf("target hash is invalid")
 89	}
 90
 91	if !text.Safe(op.Message) {
 92		return fmt.Errorf("message is not fully printable")
 93	}
 94
 95	return nil
 96}
 97
 98// Workaround to avoid the inner OpBase.MarshalJSON overriding the outer op
 99// MarshalJSON
100func (op *EditCommentOperation) MarshalJSON() ([]byte, error) {
101	base, err := json.Marshal(op.OpBase)
102	if err != nil {
103		return nil, err
104	}
105
106	// revert back to a flat map to be able to add our own fields
107	var data map[string]interface{}
108	if err := json.Unmarshal(base, &data); err != nil {
109		return nil, err
110	}
111
112	data["target"] = op.Target
113	data["message"] = op.Message
114	data["files"] = op.Files
115
116	return json.Marshal(data)
117}
118
119// Workaround to avoid the inner OpBase.MarshalJSON overriding the outer op
120// MarshalJSON
121func (op *EditCommentOperation) UnmarshalJSON(data []byte) error {
122	// Unmarshal OpBase and the op separately
123
124	base := OpBase{}
125	err := json.Unmarshal(data, &base)
126	if err != nil {
127		return err
128	}
129
130	aux := struct {
131		Target  git.Hash   `json:"target"`
132		Message string     `json:"message"`
133		Files   []git.Hash `json:"files"`
134	}{}
135
136	err = json.Unmarshal(data, &aux)
137	if err != nil {
138		return err
139	}
140
141	op.OpBase = base
142	op.Target = aux.Target
143	op.Message = aux.Message
144	op.Files = aux.Files
145
146	return nil
147}
148
149// Sign post method for gqlgen
150func (op *EditCommentOperation) IsAuthored() {}
151
152func NewEditCommentOp(author identity.Interface, unixTime int64, target git.Hash, message string, files []git.Hash) *EditCommentOperation {
153	return &EditCommentOperation{
154		OpBase:  newOpBase(EditCommentOp, author, unixTime),
155		Target:  target,
156		Message: message,
157		Files:   files,
158	}
159}
160
161// Convenience function to apply the operation
162func EditComment(b Interface, author identity.Interface, unixTime int64, target git.Hash, message string) (*EditCommentOperation, error) {
163	return EditCommentWithFiles(b, author, unixTime, target, message, nil)
164}
165
166func EditCommentWithFiles(b Interface, author identity.Interface, unixTime int64, target git.Hash, message string, files []git.Hash) (*EditCommentOperation, error) {
167	editCommentOp := NewEditCommentOp(author, unixTime, target, message, files)
168	if err := editCommentOp.Validate(); err != nil {
169		return nil, err
170	}
171	b.Append(editCommentOp)
172	return editCommentOp, nil
173}