op_create.go

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