diff options
Diffstat (limited to 'bug')
-rw-r--r-- | bug/op_set_metadata.go | 72 | ||||
-rw-r--r-- | bug/op_set_metadata_test.go | 98 | ||||
-rw-r--r-- | bug/operation.go | 34 |
3 files changed, 199 insertions, 5 deletions
diff --git a/bug/op_set_metadata.go b/bug/op_set_metadata.go new file mode 100644 index 00000000..68cf1d51 --- /dev/null +++ b/bug/op_set_metadata.go @@ -0,0 +1,72 @@ +package bug + +import "github.com/MichaelMure/git-bug/util/git" + +var _ Operation = &SetMetadataOperation{} + +type SetMetadataOperation struct { + OpBase + Target git.Hash `json:"target"` + NewMetadata map[string]string `json:"new_metadata"` +} + +func (op *SetMetadataOperation) base() *OpBase { + return &op.OpBase +} + +func (op *SetMetadataOperation) Hash() (git.Hash, error) { + return hashOperation(op) +} + +func (op *SetMetadataOperation) Apply(snapshot *Snapshot) { + for _, target := range snapshot.Operations { + hash, err := target.Hash() + if err != nil { + // Should never error unless a programming error happened + // (covered in OpBase.Validate()) + panic(err) + } + + if hash == op.Target { + base := target.base() + + if base.extraMetadata == nil { + base.extraMetadata = make(map[string]string) + } + + for key, val := range op.NewMetadata { + if _, exist := base.extraMetadata[key]; !exist { + base.extraMetadata[key] = val + } + } + + return + } + } +} + +func (op *SetMetadataOperation) Validate() error { + if err := opBaseValidate(op, SetMetadataOp); err != nil { + return err + } + + return nil +} + +func NewSetMetadataOp(author Person, unixTime int64, target git.Hash, newMetadata map[string]string) *SetMetadataOperation { + return &SetMetadataOperation{ + OpBase: newOpBase(SetMetadataOp, author, unixTime), + Target: target, + NewMetadata: newMetadata, + } +} + +// Convenience function to apply the operation +func SetMetadata(b Interface, author Person, unixTime int64, target git.Hash, newMetadata map[string]string) (*SetMetadataOperation, error) { + SetMetadataOp := NewSetMetadataOp(author, unixTime, target, newMetadata) + if err := SetMetadataOp.Validate(); err != nil { + return nil, err + } + b.Append(SetMetadataOp) + return SetMetadataOp, nil +} diff --git a/bug/op_set_metadata_test.go b/bug/op_set_metadata_test.go new file mode 100644 index 00000000..068e2bb0 --- /dev/null +++ b/bug/op_set_metadata_test.go @@ -0,0 +1,98 @@ +package bug + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +func TestSetMetadata(t *testing.T) { + snapshot := Snapshot{} + + var rene = Person{ + Name: "René Descartes", + Email: "rene@descartes.fr", + } + + unix := time.Now().Unix() + + create := NewCreateOp(rene, unix, "title", "create", nil) + create.SetMetadata("key", "value") + create.Apply(&snapshot) + snapshot.Operations = append(snapshot.Operations, create) + + hash1, err := create.Hash() + if err != nil { + t.Fatal(err) + } + + comment := NewAddCommentOp(rene, unix, "comment", nil) + comment.SetMetadata("key2", "value2") + comment.Apply(&snapshot) + snapshot.Operations = append(snapshot.Operations, comment) + + hash2, err := comment.Hash() + if err != nil { + t.Fatal(err) + } + + op1 := NewSetMetadataOp(rene, unix, hash1, map[string]string{ + "key": "override", + "key2": "value", + }) + + op1.Apply(&snapshot) + snapshot.Operations = append(snapshot.Operations, op1) + + createMetadata := snapshot.Operations[0].AllMetadata() + assert.Equal(t, len(createMetadata), 2) + // original key is not overrided + assert.Equal(t, createMetadata["key"], "value") + // new key is set + assert.Equal(t, createMetadata["key2"], "value") + + commentMetadata := snapshot.Operations[1].AllMetadata() + assert.Equal(t, len(commentMetadata), 1) + assert.Equal(t, commentMetadata["key2"], "value2") + + op2 := NewSetMetadataOp(rene, unix, hash2, map[string]string{ + "key2": "value", + "key3": "value3", + }) + + op2.Apply(&snapshot) + snapshot.Operations = append(snapshot.Operations, op2) + + createMetadata = snapshot.Operations[0].AllMetadata() + assert.Equal(t, len(createMetadata), 2) + assert.Equal(t, createMetadata["key"], "value") + assert.Equal(t, createMetadata["key2"], "value") + + commentMetadata = snapshot.Operations[1].AllMetadata() + assert.Equal(t, len(commentMetadata), 2) + // original key is not overrided + assert.Equal(t, commentMetadata["key2"], "value2") + // new key is set + assert.Equal(t, commentMetadata["key3"], "value3") + + op3 := NewSetMetadataOp(rene, unix, hash1, map[string]string{ + "key": "override", + "key2": "override", + }) + + op3.Apply(&snapshot) + snapshot.Operations = append(snapshot.Operations, op3) + + createMetadata = snapshot.Operations[0].AllMetadata() + assert.Equal(t, len(createMetadata), 2) + // original key is not overrided + assert.Equal(t, createMetadata["key"], "value") + // previously set key is not overrided + assert.Equal(t, createMetadata["key2"], "value") + + commentMetadata = snapshot.Operations[1].AllMetadata() + assert.Equal(t, len(commentMetadata), 2) + assert.Equal(t, commentMetadata["key2"], "value2") + assert.Equal(t, commentMetadata["key3"], "value3") +} diff --git a/bug/operation.go b/bug/operation.go index bb88af1f..592b5616 100644 --- a/bug/operation.go +++ b/bug/operation.go @@ -22,6 +22,7 @@ const ( LabelChangeOp EditCommentOp NoOpOp + SetMetadataOp ) // Operation define the interface to fulfill for an edit operation of a Bug @@ -75,11 +76,15 @@ func hashOperation(op Operation) (git.Hash, error) { // OpBase implement the common code for all operations type OpBase struct { - OperationType OperationType `json:"type"` - Author Person `json:"author"` - UnixTime int64 `json:"timestamp"` - hash git.Hash + OperationType OperationType `json:"type"` + Author Person `json:"author"` + UnixTime int64 `json:"timestamp"` Metadata map[string]string `json:"metadata,omitempty"` + // Not serialized. Store the op's hash in memory. + hash git.Hash + // Not serialized. Store the extra metadata compiled from SetMetadataOperation + // in memory. + extraMetadata map[string]string } // newOpBase is the constructor for an OpBase @@ -146,10 +151,29 @@ func (op *OpBase) SetMetadata(key string, value string) { // GetMetadata retrieve arbitrary metadata about the operation func (op *OpBase) GetMetadata(key string) (string, bool) { val, ok := op.Metadata[key] + + if ok { + return val, true + } + + // extraMetadata can't replace the original operations value if any + val, ok = op.extraMetadata[key] + return val, ok } // AllMetadata return all metadata for this operation func (op *OpBase) AllMetadata() map[string]string { - return op.Metadata + result := make(map[string]string) + + for key, val := range op.extraMetadata { + result[key] = val + } + + // Original metadata take precedence + for key, val := range op.Metadata { + result[key] = val + } + + return result } |