op_create.go

  1package bug
  2
  3import (
  4	"fmt"
  5
  6	"github.com/git-bug/git-bug/entities/identity"
  7	"github.com/git-bug/git-bug/entity"
  8	"github.com/git-bug/git-bug/entity/dag"
  9	"github.com/git-bug/git-bug/repository"
 10	"github.com/git-bug/git-bug/util/text"
 11	"github.com/git-bug/git-bug/util/timestamp"
 12)
 13
 14var _ Operation = &CreateOperation{}
 15var _ dag.OperationWithFiles = &CreateOperation{}
 16
 17// CreateOperation define the initial creation of a bug
 18type CreateOperation struct {
 19	dag.OpBase
 20	Title   string            `json:"title"`
 21	Message string            `json:"message"`
 22	Files   []repository.Hash `json:"files"`
 23}
 24
 25func (op *CreateOperation) Id() entity.Id {
 26	return dag.IdOperation(op, &op.OpBase)
 27}
 28
 29func (op *CreateOperation) Apply(snapshot *Snapshot) {
 30	// sanity check: will fail when adding a second Create
 31	if snapshot.id != "" && snapshot.id != entity.UnsetId && snapshot.id != op.Id() {
 32		return
 33	}
 34
 35	// the Id of the Bug/Snapshot is the Id of the first Operation: CreateOperation
 36	opId := op.Id()
 37	snapshot.id = opId
 38
 39	snapshot.addActor(op.Author())
 40	snapshot.addParticipant(op.Author())
 41
 42	snapshot.Title = op.Title
 43
 44	comment := Comment{
 45		combinedId: entity.CombineIds(snapshot.id, opId),
 46		targetId:   opId,
 47		Message:    op.Message,
 48		Author:     op.Author(),
 49		unixTime:   timestamp.Timestamp(op.UnixTime),
 50	}
 51
 52	snapshot.Comments = []Comment{comment}
 53	snapshot.Author = op.Author()
 54	snapshot.CreateTime = op.Time()
 55
 56	snapshot.Timeline = []TimelineItem{
 57		&CreateTimelineItem{
 58			CommentTimelineItem: NewCommentTimelineItem(comment),
 59		},
 60	}
 61}
 62
 63func (op *CreateOperation) GetFiles() []repository.Hash {
 64	return op.Files
 65}
 66
 67func (op *CreateOperation) Validate() error {
 68	if err := op.OpBase.Validate(op, CreateOp); err != nil {
 69		return err
 70	}
 71
 72	if text.Empty(op.Title) {
 73		return fmt.Errorf("title is empty")
 74	}
 75	if !text.SafeOneLine(op.Title) {
 76		return fmt.Errorf("title has unsafe characters")
 77	}
 78
 79	if !text.Safe(op.Message) {
 80		return fmt.Errorf("message is not fully printable")
 81	}
 82
 83	return nil
 84}
 85
 86func NewCreateOp(author identity.Interface, unixTime int64, title, message string, files []repository.Hash) *CreateOperation {
 87	return &CreateOperation{
 88		OpBase:  dag.NewOpBase(CreateOp, author, unixTime),
 89		Title:   title,
 90		Message: message,
 91		Files:   files,
 92	}
 93}
 94
 95// CreateTimelineItem replace a Create operation in the Timeline and hold its edition history
 96type CreateTimelineItem struct {
 97	CommentTimelineItem
 98}
 99
100// IsAuthored is a sign post-method for gqlgen, to mark compliance to an interface.
101func (c *CreateTimelineItem) IsAuthored() {}
102
103// Create is a convenience function to create a bug
104func Create(author identity.Interface, unixTime int64, title, message string, files []repository.Hash, metadata map[string]string) (*Bug, *CreateOperation, error) {
105	b := NewBug()
106	op := NewCreateOp(author, unixTime, title, message, files)
107	for key, val := range metadata {
108		op.SetMetadata(key, val)
109	}
110	if err := op.Validate(); err != nil {
111		return nil, op, err
112	}
113	b.Append(op)
114	return b, op, nil
115}