op_set_title.go

  1package bug
  2
  3import (
  4	"encoding/json"
  5	"fmt"
  6	"strings"
  7
  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
 20	Was   string
 21}
 22
 23func (op *SetTitleOperation) base() *OpBase {
 24	return &op.OpBase
 25}
 26
 27func (op *SetTitleOperation) ID() string {
 28	return idOperation(op)
 29}
 30
 31func (op *SetTitleOperation) Apply(snapshot *Snapshot) {
 32	snapshot.Title = op.Title
 33	snapshot.addActor(op.Author)
 34
 35	item := &SetTitleTimelineItem{
 36		id:       op.ID(),
 37		Author:   op.Author,
 38		UnixTime: timestamp.Timestamp(op.UnixTime),
 39		Title:    op.Title,
 40		Was:      op.Was,
 41	}
 42
 43	snapshot.Timeline = append(snapshot.Timeline, item)
 44}
 45
 46func (op *SetTitleOperation) Validate() error {
 47	if err := opBaseValidate(op, SetTitleOp); err != nil {
 48		return err
 49	}
 50
 51	if text.Empty(op.Title) {
 52		return fmt.Errorf("title is empty")
 53	}
 54
 55	if strings.Contains(op.Title, "\n") {
 56		return fmt.Errorf("title should be a single line")
 57	}
 58
 59	if !text.Safe(op.Title) {
 60		return fmt.Errorf("title should be fully printable")
 61	}
 62
 63	if strings.Contains(op.Was, "\n") {
 64		return fmt.Errorf("previous title should be a single line")
 65	}
 66
 67	if !text.Safe(op.Was) {
 68		return fmt.Errorf("previous title should be fully printable")
 69	}
 70
 71	return nil
 72}
 73
 74// Workaround to avoid the inner OpBase.MarshalJSON overriding the outer op
 75// MarshalJSON
 76func (op *SetTitleOperation) MarshalJSON() ([]byte, error) {
 77	base, err := json.Marshal(op.OpBase)
 78	if err != nil {
 79		return nil, err
 80	}
 81
 82	// revert back to a flat map to be able to add our own fields
 83	var data map[string]interface{}
 84	if err := json.Unmarshal(base, &data); err != nil {
 85		return nil, err
 86	}
 87
 88	data["title"] = op.Title
 89	data["was"] = op.Was
 90
 91	return json.Marshal(data)
 92}
 93
 94// Workaround to avoid the inner OpBase.MarshalJSON overriding the outer op
 95// MarshalJSON
 96func (op *SetTitleOperation) UnmarshalJSON(data []byte) error {
 97	// Unmarshal OpBase and the op separately
 98
 99	base := OpBase{}
100	err := json.Unmarshal(data, &base)
101	if err != nil {
102		return err
103	}
104
105	aux := struct {
106		Title string `json:"title"`
107		Was   string `json:"was"`
108	}{}
109
110	err = json.Unmarshal(data, &aux)
111	if err != nil {
112		return err
113	}
114
115	op.OpBase = base
116	op.Title = aux.Title
117	op.Was = aux.Was
118
119	return nil
120}
121
122// Sign post method for gqlgen
123func (op *SetTitleOperation) IsAuthored() {}
124
125func NewSetTitleOp(author identity.Interface, unixTime int64, title string, was string) *SetTitleOperation {
126	return &SetTitleOperation{
127		OpBase: newOpBase(SetTitleOp, author, unixTime),
128		Title:  title,
129		Was:    was,
130	}
131}
132
133type SetTitleTimelineItem struct {
134	id       string
135	Author   identity.Interface
136	UnixTime timestamp.Timestamp
137	Title    string
138	Was      string
139}
140
141func (s SetTitleTimelineItem) ID() string {
142	return s.id
143}
144
145// Sign post method for gqlgen
146func (s *SetTitleTimelineItem) IsAuthored() {}
147
148// Convenience function to apply the operation
149func SetTitle(b Interface, author identity.Interface, unixTime int64, title string) (*SetTitleOperation, error) {
150	it := NewOperationIterator(b)
151
152	var lastTitleOp Operation
153	for it.Next() {
154		op := it.Value()
155		if op.base().OperationType == SetTitleOp {
156			lastTitleOp = op
157		}
158	}
159
160	var was string
161	if lastTitleOp != nil {
162		was = lastTitleOp.(*SetTitleOperation).Title
163	} else {
164		was = b.FirstOp().(*CreateOperation).Title
165	}
166
167	setTitleOp := NewSetTitleOp(author, unixTime, title, was)
168
169	if err := setTitleOp.Validate(); err != nil {
170		return nil, err
171	}
172
173	b.Append(setTitleOp)
174	return setTitleOp, nil
175}