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
 24// Sign-post method for gqlgen
 25func (op *SetTitleOperation) IsOperation() {}
 26
 27func (op *SetTitleOperation) Id() entity.Id {
 28	return idOperation(op, &op.OpBase)
 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 := op.OpBase.Validate(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// UnmarshalJSON is a two step JSON unmarshalling
 75// This workaround is necessary to avoid the inner OpBase.MarshalJSON
 76// overriding the outer op's MarshalJSON
 77func (op *SetTitleOperation) UnmarshalJSON(data []byte) error {
 78	// Unmarshal OpBase and the op separately
 79
 80	base := OpBase{}
 81	err := json.Unmarshal(data, &base)
 82	if err != nil {
 83		return err
 84	}
 85
 86	aux := struct {
 87		Title string `json:"title"`
 88		Was   string `json:"was"`
 89	}{}
 90
 91	err = json.Unmarshal(data, &aux)
 92	if err != nil {
 93		return err
 94	}
 95
 96	op.OpBase = base
 97	op.Title = aux.Title
 98	op.Was = aux.Was
 99
100	return nil
101}
102
103// Sign post method for gqlgen
104func (op *SetTitleOperation) IsAuthored() {}
105
106func NewSetTitleOp(author identity.Interface, unixTime int64, title string, was string) *SetTitleOperation {
107	return &SetTitleOperation{
108		OpBase: newOpBase(SetTitleOp, author, unixTime),
109		Title:  title,
110		Was:    was,
111	}
112}
113
114type SetTitleTimelineItem struct {
115	id       entity.Id
116	Author   identity.Interface
117	UnixTime timestamp.Timestamp
118	Title    string
119	Was      string
120}
121
122func (s SetTitleTimelineItem) Id() entity.Id {
123	return s.id
124}
125
126// Sign post method for gqlgen
127func (s *SetTitleTimelineItem) IsAuthored() {}
128
129// Convenience function to apply the operation
130func SetTitle(b Interface, author identity.Interface, unixTime int64, title string) (*SetTitleOperation, error) {
131	var lastTitleOp *SetTitleOperation
132	for _, op := range b.Operations() {
133		switch op := op.(type) {
134		case *SetTitleOperation:
135			lastTitleOp = op
136		}
137	}
138
139	var was string
140	if lastTitleOp != nil {
141		was = lastTitleOp.Title
142	} else {
143		was = b.FirstOp().(*CreateOperation).Title
144	}
145
146	setTitleOp := NewSetTitleOp(author, unixTime, title, was)
147
148	if err := setTitleOp.Validate(); err != nil {
149		return nil, err
150	}
151
152	b.Append(setTitleOp)
153	return setTitleOp, nil
154}