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