op_create.go

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