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}