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