aboutsummaryrefslogtreecommitdiffstats
path: root/entity/dag/entity.go
diff options
context:
space:
mode:
Diffstat (limited to 'entity/dag/entity.go')
-rw-r--r--entity/dag/entity.go92
1 files changed, 82 insertions, 10 deletions
diff --git a/entity/dag/entity.go b/entity/dag/entity.go
index 4ccf0e0e..f9b55bd4 100644
--- a/entity/dag/entity.go
+++ b/entity/dag/entity.go
@@ -9,8 +9,8 @@ import (
"github.com/pkg/errors"
+ "github.com/MichaelMure/git-bug/entities/identity"
"github.com/MichaelMure/git-bug/entity"
- "github.com/MichaelMure/git-bug/identity"
"github.com/MichaelMure/git-bug/repository"
"github.com/MichaelMure/git-bug/util/lamport"
)
@@ -26,7 +26,7 @@ type Definition struct {
// the Namespace in git references (bugs, prs, ...)
Namespace string
// a function decoding a JSON message into an Operation
- OperationUnmarshaler func(raw json.RawMessage, resolver identity.Resolver) (Operation, error)
+ OperationUnmarshaler func(raw json.RawMessage, resolver entity.Resolvers) (Operation, error)
// the expected format version number, that can be used for data migration/upgrade
FormatVersion uint
}
@@ -57,29 +57,29 @@ func New(definition Definition) *Entity {
}
// Read will read and decode a stored local Entity from a repository
-func Read(def Definition, repo repository.ClockedRepo, resolver identity.Resolver, id entity.Id) (*Entity, error) {
+func Read(def Definition, repo repository.ClockedRepo, resolvers entity.Resolvers, id entity.Id) (*Entity, error) {
if err := id.Validate(); err != nil {
return nil, errors.Wrap(err, "invalid id")
}
ref := fmt.Sprintf("refs/%s/%s", def.Namespace, id.String())
- return read(def, repo, resolver, ref)
+ return read(def, repo, resolvers, ref)
}
// readRemote will read and decode a stored remote Entity from a repository
-func readRemote(def Definition, repo repository.ClockedRepo, resolver identity.Resolver, remote string, id entity.Id) (*Entity, error) {
+func readRemote(def Definition, repo repository.ClockedRepo, resolvers entity.Resolvers, remote string, id entity.Id) (*Entity, error) {
if err := id.Validate(); err != nil {
return nil, errors.Wrap(err, "invalid id")
}
ref := fmt.Sprintf("refs/remotes/%s/%s/%s", def.Namespace, remote, id.String())
- return read(def, repo, resolver, ref)
+ return read(def, repo, resolvers, ref)
}
// read fetch from git and decode an Entity at an arbitrary git reference.
-func read(def Definition, repo repository.ClockedRepo, resolver identity.Resolver, ref string) (*Entity, error) {
+func read(def Definition, repo repository.ClockedRepo, resolvers entity.Resolvers, ref string) (*Entity, error) {
rootHash, err := repo.ResolveRef(ref)
if err != nil {
return nil, err
@@ -138,7 +138,7 @@ func read(def Definition, repo repository.ClockedRepo, resolver identity.Resolve
return nil, fmt.Errorf("multiple leafs in the entity DAG")
}
- opp, err := readOperationPack(def, repo, resolver, commit)
+ opp, err := readOperationPack(def, repo, resolvers, commit)
if err != nil {
return nil, err
}
@@ -239,13 +239,65 @@ func read(def Definition, repo repository.ClockedRepo, resolver identity.Resolve
}, nil
}
+// readClockNoCheck fetch from git, read and witness the clocks of an Entity at an arbitrary git reference.
+// Note: readClockNoCheck does not verify the integrity of the Entity and could witness incorrect or incomplete
+// clocks if so. If data integrity check is a requirement, a flow similar to read without actually reading/decoding
+// operation blobs can be implemented instead.
+func readClockNoCheck(def Definition, repo repository.ClockedRepo, ref string) error {
+ rootHash, err := repo.ResolveRef(ref)
+ if err != nil {
+ return err
+ }
+
+ commit, err := repo.ReadCommit(rootHash)
+ if err != nil {
+ return err
+ }
+
+ createTime, editTime, err := readOperationPackClock(repo, commit)
+ if err != nil {
+ return err
+ }
+
+ // if we have more than one commit, we need to find the root to have the create time
+ if len(commit.Parents) > 0 {
+ for len(commit.Parents) > 0 {
+ // The path to the root is irrelevant.
+ commit, err = repo.ReadCommit(commit.Parents[0])
+ if err != nil {
+ return err
+ }
+ }
+ createTime, _, err = readOperationPackClock(repo, commit)
+ if err != nil {
+ return err
+ }
+ }
+
+ if createTime <= 0 {
+ return fmt.Errorf("creation lamport time not set")
+ }
+ if editTime <= 0 {
+ return fmt.Errorf("creation lamport time not set")
+ }
+ err = repo.Witness(fmt.Sprintf(creationClockPattern, def.Namespace), createTime)
+ if err != nil {
+ return err
+ }
+ err = repo.Witness(fmt.Sprintf(editClockPattern, def.Namespace), editTime)
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
type StreamedEntity struct {
Entity *Entity
Err error
}
// ReadAll read and parse all local Entity
-func ReadAll(def Definition, repo repository.ClockedRepo, resolver identity.Resolver) <-chan StreamedEntity {
+func ReadAll(def Definition, repo repository.ClockedRepo, resolvers entity.Resolvers) <-chan StreamedEntity {
out := make(chan StreamedEntity)
go func() {
@@ -260,7 +312,7 @@ func ReadAll(def Definition, repo repository.ClockedRepo, resolver identity.Reso
}
for _, ref := range refs {
- e, err := read(def, repo, resolver, ref)
+ e, err := read(def, repo, resolvers, ref)
if err != nil {
out <- StreamedEntity{Err: err}
@@ -274,6 +326,26 @@ func ReadAll(def Definition, repo repository.ClockedRepo, resolver identity.Reso
return out
}
+// ReadAllClocksNoCheck goes over all entities matching Definition and read/witness the corresponding clocks so that the
+// repo end up with correct clocks for the next write.
+func ReadAllClocksNoCheck(def Definition, repo repository.ClockedRepo) error {
+ refPrefix := fmt.Sprintf("refs/%s/", def.Namespace)
+
+ refs, err := repo.ListRefs(refPrefix)
+ if err != nil {
+ return err
+ }
+
+ for _, ref := range refs {
+ err = readClockNoCheck(def, repo, ref)
+ if err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
// Id return the Entity identifier
func (e *Entity) Id() entity.Id {
// id is the id of the first operation