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