1package bug
  2
  3import (
  4	"encoding/json"
  5	"fmt"
  6
  7	"github.com/MichaelMure/git-bug/entity"
  8	"github.com/MichaelMure/git-bug/identity"
  9	"github.com/MichaelMure/git-bug/util/timestamp"
 10
 11	"github.com/MichaelMure/git-bug/util/text"
 12)
 13
 14var _ Operation = &SetTitleOperation{}
 15
 16// SetTitleOperation will change the title of a bug
 17type SetTitleOperation struct {
 18	OpBase
 19	Title string `json:"title"`
 20	Was   string `json:"was"`
 21}
 22
 23func (op *SetTitleOperation) Id() entity.Id {
 24	return idOperation(op, &op.OpBase)
 25}
 26
 27func (op *SetTitleOperation) Apply(snapshot *Snapshot) {
 28	snapshot.Title = op.Title
 29	snapshot.addActor(op.Author_)
 30
 31	item := &SetTitleTimelineItem{
 32		id:       op.Id(),
 33		Author:   op.Author_,
 34		UnixTime: timestamp.Timestamp(op.UnixTime),
 35		Title:    op.Title,
 36		Was:      op.Was,
 37	}
 38
 39	snapshot.Timeline = append(snapshot.Timeline, item)
 40}
 41
 42func (op *SetTitleOperation) Validate() error {
 43	if err := op.OpBase.Validate(op, SetTitleOp); err != nil {
 44		return err
 45	}
 46
 47	if text.Empty(op.Title) {
 48		return fmt.Errorf("title is empty")
 49	}
 50
 51	if !text.SafeOneLine(op.Title) {
 52		return fmt.Errorf("title has unsafe characters")
 53	}
 54
 55	if !text.SafeOneLine(op.Was) {
 56		return fmt.Errorf("previous title has unsafe characters")
 57	}
 58
 59	return nil
 60}
 61
 62// UnmarshalJSON is a two step JSON unmarshalling
 63// This workaround is necessary to avoid the inner OpBase.MarshalJSON
 64// overriding the outer op's MarshalJSON
 65func (op *SetTitleOperation) UnmarshalJSON(data []byte) error {
 66	// Unmarshal OpBase and the op separately
 67
 68	base := OpBase{}
 69	err := json.Unmarshal(data, &base)
 70	if err != nil {
 71		return err
 72	}
 73
 74	aux := struct {
 75		Title string `json:"title"`
 76		Was   string `json:"was"`
 77	}{}
 78
 79	err = json.Unmarshal(data, &aux)
 80	if err != nil {
 81		return err
 82	}
 83
 84	op.OpBase = base
 85	op.Title = aux.Title
 86	op.Was = aux.Was
 87
 88	return nil
 89}
 90
 91// Sign post method for gqlgen
 92func (op *SetTitleOperation) IsAuthored() {}
 93
 94func NewSetTitleOp(author identity.Interface, unixTime int64, title string, was string) *SetTitleOperation {
 95	return &SetTitleOperation{
 96		OpBase: newOpBase(SetTitleOp, author, unixTime),
 97		Title:  title,
 98		Was:    was,
 99	}
100}
101
102type SetTitleTimelineItem struct {
103	id       entity.Id
104	Author   identity.Interface
105	UnixTime timestamp.Timestamp
106	Title    string
107	Was      string
108}
109
110func (s SetTitleTimelineItem) Id() entity.Id {
111	return s.id
112}
113
114// Sign post method for gqlgen
115func (s *SetTitleTimelineItem) IsAuthored() {}
116
117// Convenience function to apply the operation
118func SetTitle(b Interface, author identity.Interface, unixTime int64, title string) (*SetTitleOperation, error) {
119	var lastTitleOp *SetTitleOperation
120	for _, op := range b.Operations() {
121		switch op := op.(type) {
122		case *SetTitleOperation:
123			lastTitleOp = op
124		}
125	}
126
127	var was string
128	if lastTitleOp != nil {
129		was = lastTitleOp.Title
130	} else {
131		was = b.FirstOp().(*CreateOperation).Title
132	}
133
134	setTitleOp := NewSetTitleOp(author, unixTime, title, was)
135
136	if err := setTitleOp.Validate(); err != nil {
137		return nil, err
138	}
139
140	b.Append(setTitleOp)
141	return setTitleOp, nil
142}