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