1package bug
2
3import (
4 "encoding/json"
5
6 "github.com/MichaelMure/git-bug/identity"
7 "github.com/MichaelMure/git-bug/util/git"
8 "github.com/MichaelMure/git-bug/util/timestamp"
9 "github.com/pkg/errors"
10)
11
12var _ Operation = &SetStatusOperation{}
13
14// SetStatusOperation will change the status of a bug
15type SetStatusOperation struct {
16 OpBase
17 Status Status
18}
19
20func (op *SetStatusOperation) base() *OpBase {
21 return &op.OpBase
22}
23
24func (op *SetStatusOperation) Hash() (git.Hash, error) {
25 return hashOperation(op)
26}
27
28func (op *SetStatusOperation) Apply(snapshot *Snapshot) {
29 snapshot.Status = op.Status
30
31 hash, err := op.Hash()
32 if err != nil {
33 // Should never error unless a programming error happened
34 // (covered in OpBase.Validate())
35 panic(err)
36 }
37
38 item := &SetStatusTimelineItem{
39 hash: hash,
40 Author: op.Author,
41 UnixTime: timestamp.Timestamp(op.UnixTime),
42 Status: op.Status,
43 }
44
45 snapshot.Timeline = append(snapshot.Timeline, item)
46}
47
48func (op *SetStatusOperation) Validate() error {
49 if err := opBaseValidate(op, SetStatusOp); err != nil {
50 return err
51 }
52
53 if err := op.Status.Validate(); err != nil {
54 return errors.Wrap(err, "status")
55 }
56
57 return nil
58}
59
60// Workaround to avoid the inner OpBase.MarshalJSON overriding the outer op
61// MarshalJSON
62func (op *SetStatusOperation) MarshalJSON() ([]byte, error) {
63 base, err := json.Marshal(op.OpBase)
64 if err != nil {
65 return nil, err
66 }
67
68 // revert back to a flat map to be able to add our own fields
69 var data map[string]interface{}
70 if err := json.Unmarshal(base, &data); err != nil {
71 return nil, err
72 }
73
74 data["status"] = op.Status
75
76 return json.Marshal(data)
77}
78
79// Workaround to avoid the inner OpBase.MarshalJSON overriding the outer op
80// MarshalJSON
81func (op *SetStatusOperation) UnmarshalJSON(data []byte) error {
82 // Unmarshal OpBase and the op separately
83
84 base := OpBase{}
85 err := json.Unmarshal(data, &base)
86 if err != nil {
87 return err
88 }
89
90 aux := struct {
91 Status Status `json:"status"`
92 }{}
93
94 err = json.Unmarshal(data, &aux)
95 if err != nil {
96 return err
97 }
98
99 op.OpBase = base
100 op.Status = aux.Status
101
102 return nil
103}
104
105// Sign post method for gqlgen
106func (op *SetStatusOperation) IsAuthored() {}
107
108func NewSetStatusOp(author identity.Interface, unixTime int64, status Status) *SetStatusOperation {
109 return &SetStatusOperation{
110 OpBase: newOpBase(SetStatusOp, author, unixTime),
111 Status: status,
112 }
113}
114
115type SetStatusTimelineItem struct {
116 hash git.Hash
117 Author identity.Interface
118 UnixTime timestamp.Timestamp
119 Status Status
120}
121
122func (s SetStatusTimelineItem) Hash() git.Hash {
123 return s.hash
124}
125
126// Convenience function to apply the operation
127func Open(b Interface, author identity.Interface, unixTime int64) (*SetStatusOperation, error) {
128 op := NewSetStatusOp(author, unixTime, OpenStatus)
129 if err := op.Validate(); err != nil {
130 return nil, err
131 }
132 b.Append(op)
133 return op, nil
134}
135
136// Convenience function to apply the operation
137func Close(b Interface, author identity.Interface, unixTime int64) (*SetStatusOperation, error) {
138 op := NewSetStatusOp(author, unixTime, ClosedStatus)
139 if err := op.Validate(); err != nil {
140 return nil, err
141 }
142 b.Append(op)
143 return op, nil
144}