1package bug
2
3import (
4 "encoding/json"
5 "fmt"
6
7 "github.com/pkg/errors"
8
9 "github.com/MichaelMure/git-bug/entity"
10 "github.com/MichaelMure/git-bug/identity"
11 "github.com/MichaelMure/git-bug/util/text"
12)
13
14var _ Operation = &SetMetadataOperation{}
15
16type SetMetadataOperation struct {
17 OpBase
18 Target entity.Id `json:"target"`
19 NewMetadata map[string]string `json:"new_metadata"`
20}
21
22func (op *SetMetadataOperation) Id() entity.Id {
23 return idOperation(op, &op.OpBase)
24}
25
26func (op *SetMetadataOperation) Apply(snapshot *Snapshot) {
27 for _, target := range snapshot.Operations {
28 if target.Id() == op.Target {
29 // Apply the metadata in an immutable way: if a metadata already
30 // exist, it's not possible to override it.
31 for key, value := range op.NewMetadata {
32 target.setExtraMetadataImmutable(key, value)
33 }
34 return
35 }
36 }
37}
38
39func (op *SetMetadataOperation) Validate() error {
40 if err := op.OpBase.Validate(op, SetMetadataOp); err != nil {
41 return err
42 }
43
44 if err := op.Target.Validate(); err != nil {
45 return errors.Wrap(err, "target invalid")
46 }
47
48 for key, val := range op.NewMetadata {
49 if !text.SafeOneLine(key) {
50 return fmt.Errorf("metadata key is unsafe")
51 }
52 if !text.Safe(val) {
53 return fmt.Errorf("metadata value is not fully printable")
54 }
55 }
56
57 return nil
58}
59
60// UnmarshalJSON is a two step JSON unmarshalling
61// This workaround is necessary to avoid the inner OpBase.MarshalJSON
62// overriding the outer op's MarshalJSON
63func (op *SetMetadataOperation) UnmarshalJSON(data []byte) error {
64 // Unmarshal OpBase and the op separately
65
66 base := OpBase{}
67 err := json.Unmarshal(data, &base)
68 if err != nil {
69 return err
70 }
71
72 aux := struct {
73 Target entity.Id `json:"target"`
74 NewMetadata map[string]string `json:"new_metadata"`
75 }{}
76
77 err = json.Unmarshal(data, &aux)
78 if err != nil {
79 return err
80 }
81
82 op.OpBase = base
83 op.Target = aux.Target
84 op.NewMetadata = aux.NewMetadata
85
86 return nil
87}
88
89// Sign post method for gqlgen
90func (op *SetMetadataOperation) IsAuthored() {}
91
92func NewSetMetadataOp(author identity.Interface, unixTime int64, target entity.Id, newMetadata map[string]string) *SetMetadataOperation {
93 return &SetMetadataOperation{
94 OpBase: newOpBase(SetMetadataOp, author, unixTime),
95 Target: target,
96 NewMetadata: newMetadata,
97 }
98}
99
100// Convenience function to apply the operation
101func SetMetadata(b Interface, author identity.Interface, unixTime int64, target entity.Id, newMetadata map[string]string) (*SetMetadataOperation, error) {
102 SetMetadataOp := NewSetMetadataOp(author, unixTime, target, newMetadata)
103 if err := SetMetadataOp.Validate(); err != nil {
104 return nil, err
105 }
106 b.Append(SetMetadataOp)
107 return SetMetadataOp, nil
108}