op_set_title.go

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