aboutsummaryrefslogtreecommitdiffstats
path: root/bug/operation.go
diff options
context:
space:
mode:
Diffstat (limited to 'bug/operation.go')
-rw-r--r--bug/operation.go152
1 files changed, 111 insertions, 41 deletions
diff --git a/bug/operation.go b/bug/operation.go
index bdaa2016..71a5c15d 100644
--- a/bug/operation.go
+++ b/bug/operation.go
@@ -8,6 +8,7 @@ import (
"github.com/pkg/errors"
"github.com/MichaelMure/git-bug/entity"
+ "github.com/MichaelMure/git-bug/entity/dag"
"github.com/MichaelMure/git-bug/identity"
"github.com/MichaelMure/git-bug/repository"
)
@@ -29,34 +30,31 @@ const (
// Operation define the interface to fulfill for an edit operation of a Bug
type Operation interface {
- // base return the OpBase of the Operation, for package internal use
- base() *OpBase
- // Id return the identifier of the operation, to be used for back references
- Id() entity.Id
+ dag.Operation
+
+ // Type return the type of the operation
+ Type() OperationType
+
// Time return the time when the operation was added
Time() time.Time
// GetFiles return the files needed by this operation
GetFiles() []repository.Hash
// Apply the operation to a Snapshot to create the final state
Apply(snapshot *Snapshot)
- // Validate check if the operation is valid (ex: a title is a single line)
- Validate() error
// SetMetadata store arbitrary metadata about the operation
SetMetadata(key string, value string)
// GetMetadata retrieve arbitrary metadata about the operation
GetMetadata(key string) (string, bool)
// AllMetadata return all metadata for this operation
AllMetadata() map[string]string
- // GetAuthor return the author identity
- GetAuthor() identity.Interface
+
+ setExtraMetadataImmutable(key string, value string)
// sign-post method for gqlgen
IsOperation()
}
-func idOperation(op Operation) entity.Id {
- base := op.base()
-
+func idOperation(op Operation, base *OpBase) entity.Id {
if base.id == "" {
// something went really wrong
panic("op's id not set")
@@ -77,10 +75,69 @@ func idOperation(op Operation) entity.Id {
return base.id
}
+func operationUnmarshaller(author identity.Interface, raw json.RawMessage) (dag.Operation, error) {
+ var t struct {
+ OperationType OperationType `json:"type"`
+ }
+
+ if err := json.Unmarshal(raw, &t); err != nil {
+ return nil, err
+ }
+
+ var op Operation
+
+ switch t.OperationType {
+ case AddCommentOp:
+ op = &AddCommentOperation{}
+ case CreateOp:
+ op = &CreateOperation{}
+ case EditCommentOp:
+ op = &EditCommentOperation{}
+ case LabelChangeOp:
+ op = &LabelChangeOperation{}
+ case NoOpOp:
+ op = &NoOpOperation{}
+ case SetMetadataOp:
+ op = &SetMetadataOperation{}
+ case SetStatusOp:
+ op = &SetStatusOperation{}
+ case SetTitleOp:
+ op = &SetTitleOperation{}
+ default:
+ panic(fmt.Sprintf("unknown operation type %v", t.OperationType))
+ }
+
+ err := json.Unmarshal(raw, &op)
+ if err != nil {
+ return nil, err
+ }
+
+ switch op := op.(type) {
+ case *AddCommentOperation:
+ op.Author_ = author
+ case *CreateOperation:
+ op.Author_ = author
+ case *LabelChangeOperation:
+ op.Author_ = author
+ case *NoOpOperation:
+ op.Author_ = author
+ case *SetMetadataOperation:
+ op.Author_ = author
+ case *SetStatusOperation:
+ op.Author_ = author
+ case *SetTitleOperation:
+ op.Author_ = author
+ default:
+ panic(fmt.Sprintf("unknown operation type %T", op))
+ }
+
+ return op, nil
+}
+
// OpBase implement the common code for all operations
type OpBase struct {
OperationType OperationType `json:"type"`
- Author identity.Interface `json:"author"`
+ Author_ identity.Interface `json:"author"`
// TODO: part of the data model upgrade, this should eventually be a timestamp + lamport
UnixTime int64 `json:"timestamp"`
Metadata map[string]string `json:"metadata,omitempty"`
@@ -95,15 +152,15 @@ type OpBase struct {
func newOpBase(opType OperationType, author identity.Interface, unixTime int64) OpBase {
return OpBase{
OperationType: opType,
- Author: author,
+ Author_: author,
UnixTime: unixTime,
id: entity.UnsetId,
}
}
-func (op *OpBase) UnmarshalJSON(data []byte) error {
+func (base *OpBase) UnmarshalJSON(data []byte) error {
// Compute the Id when loading the op from disk.
- op.id = entity.DeriveId(data)
+ base.id = entity.DeriveId(data)
aux := struct {
OperationType OperationType `json:"type"`
@@ -122,39 +179,43 @@ func (op *OpBase) UnmarshalJSON(data []byte) error {
return err
}
- op.OperationType = aux.OperationType
- op.Author = author
- op.UnixTime = aux.UnixTime
- op.Metadata = aux.Metadata
+ base.OperationType = aux.OperationType
+ base.Author_ = author
+ base.UnixTime = aux.UnixTime
+ base.Metadata = aux.Metadata
return nil
}
+func (base *OpBase) Type() OperationType {
+ return base.OperationType
+}
+
// Time return the time when the operation was added
-func (op *OpBase) Time() time.Time {
- return time.Unix(op.UnixTime, 0)
+func (base *OpBase) Time() time.Time {
+ return time.Unix(base.UnixTime, 0)
}
// GetFiles return the files needed by this operation
-func (op *OpBase) GetFiles() []repository.Hash {
+func (base *OpBase) GetFiles() []repository.Hash {
return nil
}
// Validate check the OpBase for errors
-func opBaseValidate(op Operation, opType OperationType) error {
- if op.base().OperationType != opType {
- return fmt.Errorf("incorrect operation type (expected: %v, actual: %v)", opType, op.base().OperationType)
+func (base *OpBase) Validate(op Operation, opType OperationType) error {
+ if base.OperationType != opType {
+ return fmt.Errorf("incorrect operation type (expected: %v, actual: %v)", opType, base.OperationType)
}
if op.Time().Unix() == 0 {
return fmt.Errorf("time not set")
}
- if op.base().Author == nil {
+ if base.Author_ == nil {
return fmt.Errorf("author not set")
}
- if err := op.base().Author.Validate(); err != nil {
+ if err := op.Author().Validate(); err != nil {
return errors.Wrap(err, "author")
}
@@ -168,46 +229,55 @@ func opBaseValidate(op Operation, opType OperationType) error {
}
// SetMetadata store arbitrary metadata about the operation
-func (op *OpBase) SetMetadata(key string, value string) {
- if op.Metadata == nil {
- op.Metadata = make(map[string]string)
+func (base *OpBase) SetMetadata(key string, value string) {
+ if base.Metadata == nil {
+ base.Metadata = make(map[string]string)
}
- op.Metadata[key] = value
- op.id = entity.UnsetId
+ base.Metadata[key] = value
+ base.id = entity.UnsetId
}
// GetMetadata retrieve arbitrary metadata about the operation
-func (op *OpBase) GetMetadata(key string) (string, bool) {
- val, ok := op.Metadata[key]
+func (base *OpBase) GetMetadata(key string) (string, bool) {
+ val, ok := base.Metadata[key]
if ok {
return val, true
}
// extraMetadata can't replace the original operations value if any
- val, ok = op.extraMetadata[key]
+ val, ok = base.extraMetadata[key]
return val, ok
}
// AllMetadata return all metadata for this operation
-func (op *OpBase) AllMetadata() map[string]string {
+func (base *OpBase) AllMetadata() map[string]string {
result := make(map[string]string)
- for key, val := range op.extraMetadata {
+ for key, val := range base.extraMetadata {
result[key] = val
}
// Original metadata take precedence
- for key, val := range op.Metadata {
+ for key, val := range base.Metadata {
result[key] = val
}
return result
}
-// GetAuthor return author identity
-func (op *OpBase) GetAuthor() identity.Interface {
- return op.Author
+func (base *OpBase) setExtraMetadataImmutable(key string, value string) {
+ if base.extraMetadata == nil {
+ base.extraMetadata = make(map[string]string)
+ }
+ if _, exist := base.extraMetadata[key]; !exist {
+ base.extraMetadata[key] = value
+ }
+}
+
+// Author return author identity
+func (base *OpBase) Author() identity.Interface {
+ return base.Author_
}